لنزها و قابلیت‌های دوربین

توجه: این صفحه به بسته Camera2 اشاره دارد. توصیه می‌کنیم از CameraX استفاده کنید، مگراینکه برنامه‌تان به ویژگی‌های خاص و سطح پایین Camera2 نیاز داشته باشد. ‫CameraX و Camera2 هر دو از Android 5.0 (سطح میانای برنامه‌سازی کاربردی ۲۱) و بالاتر پشتیبانی می‌کنند.

بسیاری از دستگاه‌های Android مدرن دو یا چند دوربین در جلو، پشت، یا هر دو طرف دستگاه دارند. هر لنز می‌تواند قابلیت‌های منحصربه‌فردی داشته باشد، مانند ضبط رگباری، کنترل دستی، یا ردیابی حرکت. برنامه‌ای برای واریز چک ممکن است فقط از اولین دوربین پشت استفاده کند، درحالی‌که برنامه رسانه‌های اجتماعی ممکن است به‌طور پیش‌فرض از دوربین جلو استفاده کند، اما به کاربران گزینه جابه‌جایی بین همه لنزهای دردسترس را بدهد. همچنین می‌تواند انتخاب‌های آن‌ها را به‌خاطر بسپارد.

این صفحه نحوه فهرست کردن لنزهای دوربین و قابلیت‌های آن‌ها را پوشش می‌دهد تا بتوانید در برنامه‌تان تصمیم بگیرید که در یک موقعیت خاص از کدام لنز استفاده کنید. تکه‌کد زیر فهرستی از همه دوربین‌ها را بازیابی می‌کند و آن‌ها را تکرار می‌کند:

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 ذخیره می‌شود.

انتخاب پیش‌فرض‌های معقول

در برنامه‌تان، احتمالاً می‌خواهید دوربین خاصی را به‌طور پیش‌فرض باز کنید (اگر دردسترس باشد). برای مثال، برنامه سلفی احتمالاً دوربین جلو را باز می‌کند، درحالی‌که برنامه واقعیت افزوده ممکن است با دوربین پشت شروع شود. تابع زیر اولین دوربینی را که رو به جهت مشخص‌شده است برمی‌گرداند:

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» که دکمه تعویض دوربین را نشان می‌دهد.
شکل ۱. دکمه «عوض کردن دوربین» در برنامه «دوربین Google»

بسیاری از دستگاه‌ها چندین دوربین دارند که در یک جهت قرار گرفته‌اند. برخی‌از آن‌ها حتی دوربین USB خارجی دارند. برای ارائه میانای کاربری به کاربران که به آن‌ها امکان می‌دهد بین دوربین‌های مختلف جابه‌جا شوند، اولین دوربین دردسترس را برای هر پیکربندی ممکن جهت لنز انتخاب کنید.

اگرچه منطق جهانی برای انتخاب دوربین بعدی وجود ندارد، کد زیر برای اکثر موارد استفاده کار می‌کند:

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() برمی‌گرداند به پیاده‌سازی سازنده اصلی بستگی دارد. اگر چنددوربین منطقی در سیستم وجود داشته باشد، برای حفظ سازگاری برنامه با نسخه قدیمی، این روش فقط یک دوربین را برای هر گروه دوربین منطقی و دوربین‌های فیزیکی زیربنایی نشان می‌دهد. برای دیدن همه دوربین‌ها، از Camera2 API استفاده کنید.

برای اطلاعات بیشتر درباره جهت‌های دوربین، Camera.CameraInfo.orientation را ببینید.

به‌طور کلی، از Camera.getCameraInfo() API برای پرسمان همه orientationهای دوربین استفاده کنید و فقط یک دوربین را برای هر جهت موجود به کاربرانی که بین دوربین‌ها جابه‌جا می‌شوند نشان دهید.

سازگار با همه انواع دستگاه‌ها

فرض نکنید که برنامه‌تان همیشه در دستگاه دستی با یک یا دو دوربین اجرا می‌شود. درعوض، مناسب‌ترین دوربین‌ها را برای برنامه انتخاب کنید. اگر به دوربین خاصی نیاز ندارید، اولین دوربینی را که به‌سمت جهت انتخاب‌شده است انتخاب کنید. اگر دوربینی که به جهت خاصی اشاره می‌کند دردسترس نیست، بررسی کنید که آیا کاربر می‌تواند سفرش را با دوربین دیگری تکمیل کند یا نه. دردسترس بودن برنامه‌تان را در دستگاه‌های خاص براساس سخت‌افزار دوربین محدود نکنید. اگر دوربین خارجی متصل باشد، کاربر احتمالاً آن را به‌عنوان پیش‌فرض ترجیح می‌دهد.