หน้านี้ครอบคลุมสถาปัตยกรรมของ CameraX ซึ่งรวมถึงโครงสร้าง วิธีทำงานกับ API วิธีทำงานกับวงจร และวิธีรวม Use Case
โครงสร้าง CameraX
คุณสามารถใช้ CameraX เพื่อเชื่อมต่อกับกล้องของอุปกรณ์ผ่านการแยกส่วนที่เรียกว่า Use Case กรณีการใช้งานที่พร้อมใช้งานมีดังนี้
- Preview: ยอมรับแพลตฟอร์มสำหรับการแสดงตัวอย่าง เช่น
PreviewView - การวิเคราะห์รูปภาพ: มีบัฟเฟอร์ที่ CPU เข้าถึงได้สำหรับการวิเคราะห์ เช่น สำหรับแมชชีนเลิร์นนิง
- การจับภาพ: จับภาพและบันทึกรูปภาพ
- การจับภาพวิดีโอ: จับภาพวิดีโอและเสียงด้วย
VideoCapture
คุณสามารถรวม Use Case และใช้งานพร้อมกันได้ ตัวอย่างเช่น แอปสามารถให้ผู้ใช้ดูรูปภาพที่กล้องเห็นโดยใช้ Use Case ของตัวอย่าง มี Use Case ของการวิเคราะห์รูปภาพที่ระบุว่าผู้คนในรูปภาพยิ้มอยู่หรือไม่ และรวม Use Case ของการจับภาพเพื่อถ่ายภาพเมื่อผู้คนยิ้ม
โมเดล API
หากต้องการทำงานกับไลบรารี คุณต้องระบุสิ่งต่อไปนี้
- กรณีการใช้งานที่ต้องการพร้อมตัวเลือกการกำหนดค่า
- สิ่งที่ต้องทำกับข้อมูลเอาต์พุตโดยการแนบ Listener
- ขั้นตอนที่ต้องการ เช่น เวลาที่ควรเปิดใช้กล้องและเวลาที่ควรสร้างข้อมูล โดยการเชื่อมโยง Use Case กับวงจรของสถาปัตยกรรม Android
การเขียนแอป CameraX มี 2 วิธี ได้แก่ CameraController (เหมาะอย่างยิ่งหากคุณต้องการวิธีที่ง่ายที่สุดในการใช้ CameraX) หรือ CameraProvider (เหมาะอย่างยิ่งหากคุณต้องการความยืดหยุ่นมากขึ้น)
CameraController
CameraController มีฟังก์ชันหลักของ CameraX ส่วนใหญ่ในคลาสเดียว โดยใช้โค้ดการตั้งค่าเพียงเล็กน้อย และจะจัดการการเริ่มต้นกล้อง การจัดการ Use Case การหมุนเป้าหมาย การแตะเพื่อโฟกัส การบีบเพื่อซูม และอื่นๆ โดยอัตโนมัติ คลาสที่เป็นรูปธรรมซึ่งขยาย CameraController คือ LifecycleCameraController
val previewView: PreviewView = viewBinding.previewView var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA previewView.controller = cameraController
UseCase เริ่มต้นสำหรับ CameraController คือ Preview, ImageCapture และ ImageAnalysis หากต้องการปิด ImageCapture หรือ ImageAnalysis หรือเปิด VideoCapture ให้ใช้เมธอด setEnabledUseCases()
ดูการใช้งาน CameraController เพิ่มเติมได้ที่ตัวอย่างตัวสแกนคิวอาร์โค้ดหรือวิดีโอข้อมูลเบื้องต้นเกี่ยวกับ CameraController
CameraProvider
CameraProvider ยังคงใช้งานง่าย แต่เนื่องจากนักพัฒนาแอปเป็นผู้จัดการการตั้งค่าส่วนใหญ่ จึงมีโอกาสปรับแต่งการกำหนดค่าได้มากขึ้น เช่น การเปิดใช้การหมุนรูปภาพเอาต์พุตหรือการตั้งค่ารูปแบบรูปภาพเอาต์พุตใน ImageAnalysis นอกจากนี้ คุณยังใช้ Surface ที่กำหนดเองสำหรับการแสดงตัวอย่างจากกล้องได้ด้วย ซึ่งช่วยให้มีความยืดหยุ่นมากขึ้น ขณะที่ CameraController กำหนดให้คุณต้องใช้ PreviewView การใช้โค้ด Surface ที่มีอยู่อาจเป็นประโยชน์หากเป็นอินพุตไปยังส่วนอื่นๆ ของแอปอยู่แล้ว
คุณกำหนดค่ากรณีการใช้งานโดยใช้เมธอด set() และสรุปผลด้วยเมธอด build() ออบเจ็กต์กรณีการใช้งานแต่ละรายการจะมีชุด API เฉพาะสำหรับกรณีการใช้งาน ตัวอย่างเช่น กรณีการใช้งานการจับภาพจะมีการเรียกใช้เมธอด takePicture()
แทนที่แอปพลิเคชันจะวางการเรียกใช้เมธอดเริ่มต้นและหยุดที่เฉพาะเจาะจงใน onResume() และ onPause() แอปพลิเคชันจะระบุวงจรเพื่อเชื่อมโยงกล้องโดยใช้ cameraProvider.bindToLifecycle()
จากนั้นวงจรดังกล่าวจะแจ้งให้ CameraX ทราบว่าควรกำหนดค่าเซสชันการจับภาพของกล้องเมื่อใด และช่วยให้มั่นใจว่าสถานะของกล้องจะเปลี่ยนแปลงอย่างเหมาะสมเพื่อให้สอดคล้องกับการเปลี่ยนผ่านของวงจร
ดูขั้นตอนการใช้งานสำหรับ Use Case แต่ละกรณีได้ที่ใช้ตัวอย่าง, วิเคราะห์รูปภาพ, Image Capture และจับภาพวิดีโอ
กรณีการใช้งานตัวอย่างจะโต้ตอบกับ Surface เพื่อแสดงผล แอปพลิเคชันจะสร้าง Use Case ที่มีตัวเลือกการกำหนดค่าโดยใช้โค้ดต่อไปนี้
val preview = Preview.Builder().build() val viewFinder: PreviewView = findViewById(R.id.previewView) // The use case is bound to an Android Lifecycle with the following code. val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // PreviewView creates a surface provider and is the recommended provider. preview.setSurfaceProvider(viewFinder.getSurfaceProvider())
ดูโค้ดตัวอย่างเพิ่มเติมได้ที่แอปตัวอย่าง CameraX อย่างเป็นทางการ
วงจรของ CameraX
CameraX จะสังเกตวงจรเพื่อกำหนดเวลาเปิดกล้อง เวลาสร้างเซสชันการจับภาพ และเวลาหยุดและปิดเครื่อง API กรณีการใช้งานจะมีการเรียกใช้เมธอดและ Callback เพื่อตรวจสอบความคืบหน้า
ตามที่อธิบายไว้ในรวมกรณีการใช้งาน คุณสามารถเชื่อมโยงกรณีการใช้งานบางรายการเข้ากับวงจรเดียวได้ เมื่อแอปของคุณต้องรองรับ Use Case ที่ไม่สามารถรวมกันได้ คุณสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้
- จัดกลุ่มกรณีการใช้งานที่เข้ากันได้ไว้ด้วยกันใน Fragment มากกว่า 1 รายการ แล้วสลับไปมาระหว่าง Fragment
- สร้างคอมโพเนนต์วงจรที่กำหนดเองและใช้เพื่อควบคุมวงจรของกล้องด้วยตนเอง
หากคุณแยก LifecycleOwner ของ Use Case ของมุมมองและกล้องออกจากกัน (เช่น หากคุณใช้วงจรที่กำหนดเองหรือ Fragment ที่คงไว้) คุณต้องตรวจสอบว่า Use Case ทั้งหมดไม่ได้เชื่อมโยงกับ CameraX โดยใช้ ProcessCameraProvider.unbindAll() หรือโดยการยกเลิกการเชื่อมโยง Use Case แต่ละรายการแยกกัน หรือเมื่อคุณเชื่อมโยง Use Case กับวงจร คุณสามารถให้ CameraX จัดการการเปิดและปิดเซสชันการจับภาพและการยกเลิกการเชื่อมโยง Use Case ได้
หากฟังก์ชันการทำงานทั้งหมดของกล้องสอดคล้องกับวงจรของคอมโพเนนต์ที่รับรู้วงจรรายการเดียว เช่น AppCompatActivity หรือ Fragment AppCompat การใช้วงจรของคอมโพเนนต์นั้นเมื่อเชื่อมโยง Use Case ที่ต้องการทั้งหมดจะช่วยให้มั่นใจได้ว่าฟังก์ชันการทำงานของกล้องจะพร้อมใช้งานเมื่อคอมโพเนนต์ที่รับรู้วงจรทำงานอยู่ และจะถูกกำจัดอย่างปลอดภัยโดยไม่ใช้ทรัพยากรใดๆ ในกรณีอื่นๆ
LifecycleOwner ที่กำหนดเอง
สำหรับกรณีขั้นสูง คุณสามารถสร้าง LifecycleOwner ที่กำหนดเองเพื่อให้แอปควบคุมวงจรเซสชัน CameraX ได้อย่างชัดเจนแทนที่จะผูกกับ LifecycleOwner ของ Android มาตรฐาน
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้าง LifecycleOwner ที่กำหนดเองแบบง่ายๆ
class CustomLifecycle : LifecycleOwner { private val lifecycleRegistry: LifecycleRegistry init { lifecycleRegistry = LifecycleRegistry(this) lifecycleRegistry.currentState = Lifecycle.State.CREATED } // ... fun doOnResume() { lifecycleRegistry.currentState = State.RESUMED } // ... override val lifecycle: Lifecycle get() = lifecycleRegistry }
เมื่อใช้ LifecycleOwner นี้ แอปของคุณจะวางการเปลี่ยนสถานะที่จุดที่ต้องการในโค้ดได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ฟังก์ชันนี้ในแอปได้ที่การใช้ LifecycleOwner ที่กำหนดเอง
กรณีการใช้งานพร้อมกัน
กรณีการใช้งานสามารถทำงานพร้อมกันได้ แม้ว่ากรณีการใช้งานจะเชื่อมโยงกับวงจรตามลำดับได้ แต่การเชื่อมโยงกรณีการใช้งานทั้งหมดด้วยการเรียก CameraProcessProvider.bindToLifecycle() เพียงครั้งเดียวจะดีกว่า ดูข้อมูลเพิ่มเติมเกี่ยวกับแนวทางปฏิบัติแนะนำสำหรับการเปลี่ยนแปลงการกำหนดค่าได้ที่จัดการการเปลี่ยนแปลงการกำหนดค่า
ในตัวอย่างโค้ดต่อไปนี้ แอปจะระบุกรณีการใช้งาน 2 กรณีที่จะสร้างและเรียกใช้พร้อมกัน นอกจากนี้ยังระบุวงจรที่จะใช้สำหรับทั้ง 2 Use Case เพื่อให้ทั้ง 2 Use Case เริ่มต้นและหยุดตามวงจร
private lateinit var imageCapture: ImageCapture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener( Runnable { // Camera provider is now guaranteed to be available. val cameraProvider = cameraProviderFuture.get() // Set up the preview use case to display camera preview. val preview = Preview.Builder().build() // Set up the capture use case to allow users to take photos. imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() // Choose the camera by requiring a lens facing. val cameraSelector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_FRONT) .build() // Attach use cases to the camera with the same lifecycle owner. val camera = cameraProvider.bindToLifecycle( this as LifecycleOwner, cameraSelector, preview, imageCapture ) // Connect the preview use case to the previewView. preview.setSurfaceProvider( previewView.getSurfaceProvider() ) }, ContextCompat.getMainExecutor(this) ) }
CameraX ช่วยให้ใช้ Preview, VideoCapture, ImageAnalysis และ ImageCapture ได้พร้อมกันอย่างละ 1 อินสแตนซ์ นอกจากนี้
- กรณีการใช้งานแต่ละกรณีสามารถทำงานได้ด้วยตัวเอง เช่น แอปสามารถบันทึกวิดีโอได้โดยไม่ต้องใช้ตัวอย่าง
- เมื่อเปิดใช้ส่วนขยาย ระบบจะรับประกันการทำงานเฉพาะเมื่อใช้
ImageCaptureและPreviewร่วมกันเท่านั้น ทั้งนี้ขึ้นอยู่กับการติดตั้งใช้งานของ OEM โดยอาจเพิ่มImageAnalysisไม่ได้ และไม่สามารถเปิดใช้ส่วนขยายสำหรับ Use CaseVideoCaptureดูรายละเอียดได้ที่เอกสารอ้างอิงส่วนขยาย - กล้องบางตัวอาจรองรับชุดค่าผสมที่โหมดความละเอียดต่ำกว่า แต่ไม่รองรับชุดค่าผสมเดียวกันที่ความละเอียดสูงกว่า ทั้งนี้ขึ้นอยู่กับความสามารถของกล้อง
- ในอุปกรณ์ที่มีระดับฮาร์ดแวร์กล้อง
FULLหรือต่ำกว่า การรวมPreview,VideoCaptureและImageCaptureหรือImageAnalysisอาจบังคับให้ CameraX ทำซ้ำสตรีมPRIVของกล้องสำหรับPreviewและVideoCaptureการทำซ้ำนี้เรียกว่าการแชร์สตรีม ซึ่งช่วยให้ใช้ฟีเจอร์เหล่านี้พร้อมกันได้ แต่ก็ต้องแลกมาด้วยความต้องการในการประมวลผลที่เพิ่มขึ้น คุณอาจพบว่าเวลาในการตอบสนองนานขึ้นเล็กน้อยและระยะเวลาการใช้งานแบตเตอรี่ลดลง
คุณสามารถเรียกข้อมูลระดับฮาร์ดแวร์ที่รองรับได้จาก Camera2CameraInfo ตัวอย่างเช่น โค้ดต่อไปนี้จะตรวจสอบว่ากล้องหลังเริ่มต้นเป็นอุปกรณ์ LEVEL_3 หรือไม่
@androidx.annotation.OptIn(ExperimentalCamera2Interop::class) fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.availableCameraInfos) .firstOrNull() ?.let { Camera2CameraInfo.from(it) } ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 } return false }
สิทธิ์
แอปของคุณจะต้องมีสิทธิ์ CAMERA หากต้องการบันทึกรูปภาพลงในไฟล์ คุณจะต้องมีสิทธิ์ WRITE_EXTERNAL_STORAGE ด้วย ยกเว้นในอุปกรณ์ที่ใช้ Android 10 ขึ้นไป
ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าสิทธิ์สำหรับแอปได้ที่ขอสิทธิ์ของแอป
ข้อกำหนด
CameraX มีข้อกำหนดเวอร์ชันขั้นต่ำดังต่อไปนี้
- Android API ระดับ 21
- คอมโพเนนต์สถาปัตยกรรมของ Android 1.1.1
สำหรับกิจกรรมที่คำนึงถึงวงจร ให้ใช้ FragmentActivity หรือ AppCompatActivity
ประกาศทรัพยากร Dependency
หากต้องการเพิ่มทรัพยากร Dependency ใน CameraX คุณต้องเพิ่มที่เก็บ Google Maven ลงในโปรเจ็กต์
เปิดไฟล์ settings.gradle สำหรับโปรเจ็กต์ของคุณ แล้วเพิ่มที่เก็บ google() ดังที่แสดงต่อไปนี้
ดึงดูด
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
เพิ่มโค้ดต่อไปนี้ที่ส่วนท้ายของบล็อก Android
ดึงดูด
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
Kotlin
android { compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
เพิ่มโค้ดต่อไปนี้ลงในไฟล์ build.gradle ของแต่ละโมดูลสำหรับแอป
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.7.0-rc01" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" // If you want to additionally use the CameraX Lifecycle library implementation "androidx.camera:camera-lifecycle:${camerax_version}" // If you want to additionally use the CameraX VideoCapture library implementation "androidx.camera:camera-video:${camerax_version}" // If you want to additionally use the CameraX View class implementation "androidx.camera:camera-view:${camerax_version}" // If you want to additionally add CameraX ML Kit Vision Integration implementation "androidx.camera:camera-mlkit-vision:${camerax_version}" // If you want to additionally use the CameraX Extensions library implementation "androidx.camera:camera-extensions:${camerax_version}" }
Kotlin
dependencies { // CameraX core library using the camera2 implementation val camerax_version = "1.7.0-rc01" // The following line is optional, as the core library is included indirectly by camera-camera2 implementation("androidx.camera:camera-core:${camerax_version}") implementation("androidx.camera:camera-camera2:${camerax_version}") // If you want to additionally use the CameraX Lifecycle library implementation("androidx.camera:camera-lifecycle:${camerax_version}") // If you want to additionally use the CameraX VideoCapture library implementation("androidx.camera:camera-video:${camerax_version}") // If you want to additionally use the CameraX View class implementation("androidx.camera:camera-view:${camerax_version}") // If you want to additionally add CameraX ML Kit Vision Integration implementation("androidx.camera:camera-mlkit-vision:${camerax_version}") // If you want to additionally use the CameraX Extensions library implementation("androidx.camera:camera-extensions:${camerax_version}") }
ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าแอปให้เป็นไปตามข้อกำหนดเหล่านี้ได้ที่การประกาศทรัพยากร Dependency
ความสามารถในการทำงานร่วมกันของ CameraX กับ Camera2
CameraX สร้างขึ้นบน Camera2 และ CameraX แสดงวิธีอ่านและเขียนพร็อพเพอร์ตี้ในการติดตั้งใช้งาน Camera2 ดูรายละเอียดทั้งหมดได้ที่แพ็กเกจ Interop
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีกำหนดค่าพร็อพเพอร์ตี้ Camera2 ของ CameraX ได้โดยใช้ Camera2CameraInfo เพื่ออ่าน CameraCharacteristics ที่เกี่ยวข้อง นอกจากนี้ คุณยังเลือกเขียนพร็อพเพอร์ตี้ Camera2 ที่สำคัญได้ด้วยวิธีใดวิธีหนึ่งจาก 2 วิธีต่อไปนี้
ใช้
Camera2CameraControlซึ่งช่วยให้คุณตั้งค่าพร็อพเพอร์ตี้ในCaptureRequestที่สำคัญได้ เช่น โหมดโฟกัสอัตโนมัติขยาย
UseCaseของ CameraX ด้วยCamera2Interop.Extenderซึ่งช่วยให้คุณตั้งค่าพร็อพเพอร์ตี้ใน CaptureRequest ได้เช่นเดียวกับCamera2CameraControlนอกจากนี้ ยังให้การควบคุมเพิ่มเติมบางอย่าง เช่น การตั้งค่า Use Case ของสตรีมเพื่อเพิ่มประสิทธิภาพกล้องสำหรับสถานการณ์การใช้งานของคุณ ดูข้อมูลเพิ่มเติมได้ที่ใช้ Use Case ของสตรีมเพื่อประสิทธิภาพที่ดีขึ้น
ตัวอย่างโค้ดต่อไปนี้ใช้ Use Case ของสตรีมเพื่อเพิ่มประสิทธิภาพสำหรับวิดีโอคอล
ใช้ Camera2CameraInfo เพื่อดึงข้อมูลว่ากรณีการใช้งานสตรีมวิดีโอคอลพร้อมใช้งานหรือไม่ จากนั้นใช้
Camera2Interop.Extender
เพื่อตั้งค่ากรณีการใช้งานสตรีมที่สำคัญ
// Set underlying Camera2 stream use case to optimize for video calls. val videoCallStreamId = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong() // Check available CameraInfos to find the first one that supports // the video call stream use case. val frontCameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val isVideoCallStreamingSupported = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES )?.contains(videoCallStreamId) val isFrontFacing = (cameraInfo.getLensFacing() == CameraSelector.LENS_FACING_FRONT) (isVideoCallStreamingSupported == true) && isFrontFacing } val cameraSelector = frontCameraInfo.cameraSelector // Start with a Preview Builder. val previewBuilder = Preview.Builder() .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation) // Use Camera2Interop.Extender to set the video call stream use case. Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId) // Bind the Preview UseCase and the corresponding CameraSelector. val preview = previewBuilder.build() camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ CameraX ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้
Codelab
ตัวอย่างโค้ด