เลนส์กล้องและความสามารถของกล้อง

หมายเหตุ: หน้านี้อ้างอิงถึงแพ็กเกจ Camera2 เราขอแนะนำให้ใช้ CameraX เว้นแต่ว่าแอปของคุณต้องใช้ฟีเจอร์ระดับต่ำที่เฉพาะเจาะจงจาก Camera2 ทั้ง CameraX และ Camera2 รองรับ Android 5.0 (API ระดับ 21) ขึ้นไป

อุปกรณ์ Android รุ่นใหม่ๆ จำนวนมากมีกล้อง 2 ตัวขึ้นไปที่ด้านหน้า ด้านหลัง หรือทั้ง 2 ด้านของอุปกรณ์ เลนส์แต่ละตัวอาจมีความสามารถเฉพาะตัว เช่น การจับภาพต่อเนื่อง การควบคุมด้วยตนเอง หรือการติดตามการเคลื่อนไหว แอปสำหรับการฝากเช็คอาจใช้เฉพาะกล้องหลังตัวแรก ในขณะที่แอปโซเชียลมีเดียอาจใช้กล้องหน้าเป็นค่าเริ่มต้น แต่ให้ตัวเลือกแก่ผู้ใช้ในการสลับระหว่างเลนส์ทั้งหมดที่ใช้ได้ และยังจดจำตัวเลือกของผู้ใช้ได้ด้วย

หน้านี้จะอธิบายวิธีแสดงรายการเลนส์กล้องและความสามารถของเลนส์ เพื่อให้คุณตัดสินใจภายในแอปได้ว่าจะใช้เลนส์ใดในสถานการณ์หนึ่งๆ ข้อมูลโค้ดต่อไปนี้จะดึงข้อมูลรายการกล้องทั้งหมดและทำซ้ำรายการกล้องเหล่านั้น

try {
    val cameraIdList = cameraManager.cameraIdList // may be empty

    // iterate over available camera devices.
    for (cameraId in cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
        val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API.
        val isBackwardCompatible = cameraCapabilities?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
        // ...
    }
} catch (e: CameraAccessException) {
    e.message?.let { Log.e(TAG, it) }
    // ...
}

ตัวแปร cameraLensFacing อธิบายทิศทางที่กล้องหันไปเมื่อเทียบกับหน้าจออุปกรณ์ และมีค่าใดค่าหนึ่งต่อไปนี้

ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่าการหันหน้าของเลนส์ได้ที่ CameraCharacteristics.LENS_FACING

ตัวแปร cameraCapabilities จากตัวอย่างโค้ดก่อนหน้ามีข้อมูลเกี่ยวกับความสามารถเบ็ดเตล็ด ซึ่งรวมถึงความสามารถของกล้องในการสร้างเฟรมมาตรฐานเป็นเอาต์พุต (ตรงข้ามกับข้อมูลเซ็นเซอร์ความลึกเท่านั้น เป็นต้น) คุณสามารถดูว่า CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE เป็นหนึ่งในความสามารถที่แสดงอยู่ของกล้องหรือไม่ ซึ่งจะจัดเก็บเป็นแฟล็กใน isBackwardCompatible

เลือกค่าเริ่มต้นที่เหมาะสม

ในแอป คุณอาจต้องการเปิดกล้องที่เฉพาะเจาะจงโดยค่าเริ่มต้น (หากมี) ตัวอย่างเช่น แอปเซลฟีมีแนวโน้มที่จะเปิดกล้องหน้า ในขณะที่แอปเทคโนโลยีความจริงเสริม (AR) อาจเริ่มด้วยกล้องหลัง ฟังก์ชันต่อไปนี้จะแสดงผลกล้องตัวแรกที่หันไปในทิศทางที่กำหนด

fun getFirstCameraIdFacing(
    cameraManager: CameraManager,
    facing: Int = CameraMetadata.LENS_FACING_BACK
): String? {
    try {
        // Get list of all compatible cameras.
        val cameraIds = cameraManager.cameraIdList.filter {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
            capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
            ) ?: false
        }

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration.
        cameraIds.forEach {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
                return it
            }
        }

        // If no camera matched desired orientation, return the first one from the list.
        return cameraIds.firstOrNull()
    } catch (e: CameraAccessException) {
        e.message?.let { Log.e(TAG, it) }
        return null
    }
}

เปิดใช้การสลับกล้อง

แอปกล้องหลายแอปมีตัวเลือกให้ผู้ใช้สลับระหว่างกล้องต่างๆ ดังนี้

อินเทอร์เฟซของแอป Google กล้องถ่ายรูปที่แสดงปุ่มสลับกล้อง
รูปที่ 1 ปุ่มสลับกล้องในแอป Google กล้องถ่ายรูป

อุปกรณ์หลายเครื่องมีกล้องหลายตัวที่หันไปในทิศทางเดียวกัน บางรุ่นยังมีกล้อง USB ภายนอกด้วย หากต้องการให้ผู้ใช้มี UI ที่ช่วยให้สลับระหว่างกล้องที่หันไปคนละด้านได้ ให้เลือกกล้องตัวแรกที่พร้อมใช้งานสำหรับการกำหนดค่าการหันเลนส์ที่เป็นไปได้แต่ละแบบ

แม้ว่าจะไม่มีตรรกะสากลสำหรับการเลือกกล้องถัดไป แต่โค้ดต่อไปนี้ใช้ได้กับ Use Case ส่วนใหญ่

fun filterCompatibleCameras(
    cameraIds: Array<String>,
    cameraManager: CameraManager
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
    }
}

fun filterCameraIdsFacing(
    cameraIds: List<String>,
    cameraManager: CameraManager,
    facing: Int
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists.
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK
    )
    val frontCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT
    )
    val externalCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL
    )

    // The recommended order of iteration is: all external, first back, first front.
    val allCameras = (
        externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull()
        )
        ).filterNotNull()

    // Get the index of the currently selected camera in the list.
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be in the list, for example it could be an
    // external camera that has been removed by the user.
    return if (cameraIndex == -1) {
        // Return the first camera from the list.
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary.
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

โค้ดนี้ใช้ได้กับอุปกรณ์จำนวนมากที่มีการกำหนดค่าที่แตกต่างกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับการพิจารณากรณีที่เกิดขึ้นไม่บ่อยได้ที่ CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA

สร้างแอปที่เข้ากันได้

สำหรับแอปที่ยังคงใช้ Camera API ที่เลิกใช้งานแล้ว จำนวนกล้องที่ Camera.getNumberOfCameras() แสดงผลจะขึ้นอยู่กับการติดตั้งใช้งาน OEM หากมีกล้องหลายตัวเชิงตรรกะในระบบ เพื่อรักษาความเข้ากันได้แบบย้อนหลังของแอป วิธีนี้จะแสดงกล้องเพียงตัวเดียวสำหรับกล้องเชิงตรรกะและกลุ่มกล้องจริงที่เกี่ยวข้องทุกกลุ่ม ใช้ Camera2 API เพื่อดูกล้องทั้งหมด

ดูข้อมูลพื้นฐานเพิ่มเติมเกี่ยวกับการวางแนวกล้องได้ที่ Camera.CameraInfo.orientation

โดยทั่วไป ให้ใช้ API Camera.getCameraInfo() เพื่อค้นหา orientation ของกล้องทั้งหมด และแสดงกล้องเพียงตัวเดียวสำหรับการวางแนวที่ใช้ได้แต่ละแบบแก่ผู้ใช้ที่สลับไปมาระหว่างกล้อง

รองรับอุปกรณ์ทุกประเภท

อย่าคิดไปเองว่าแอปจะทำงานบนอุปกรณ์พกพาที่มีกล้อง 1 หรือ 2 ตัวเสมอไป แต่ให้เลือกกล้องที่เหมาะสมที่สุดสำหรับแอปแทน หากคุณไม่ต้องการกล้องที่เฉพาะเจาะจง ให้เลือกกล้องตัวแรกที่หันไปในทิศทางที่เลือก หากกล้องที่หันไปในทิศทางที่ต้องการไม่พร้อมใช้งาน ให้พิจารณาว่าผู้ใช้จะเดินทางให้เสร็จสมบูรณ์ด้วยกล้องอื่นได้หรือไม่ อย่าจำกัดความพร้อมให้บริการของแอปในอุปกรณ์บางรุ่นโดยอิงตามฮาร์ดแวร์กล้อง หากมีการเชื่อมต่อกล้องภายนอก ผู้ใช้อาจต้องการใช้กล้องดังกล่าวเป็นค่าเริ่มต้น