สถาปัตยกรรม CameraX

หน้านี้ครอบคลุมสถาปัตยกรรมของ 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 Case VideoCapture ดูรายละเอียดได้ที่เอกสารอ้างอิงส่วนขยาย
  • กล้องบางตัวอาจรองรับชุดค่าผสมที่โหมดความละเอียดต่ำกว่า แต่ไม่รองรับชุดค่าผสมเดียวกันที่ความละเอียดสูงกว่า ทั้งนี้ขึ้นอยู่กับความสามารถของกล้อง
  • ในอุปกรณ์ที่มีระดับฮาร์ดแวร์กล้อง 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

  • เริ่มต้นใช้งาน CameraX
  • ตัวอย่างโค้ด

  • แอปตัวอย่าง CameraX