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

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

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

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

کاتلین

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) }
    ...
}

جاوا

try {
    String[] cameraIdList = cameraManager.getCameraIdList(); // may be empty

    // iterate over available camera devices
    for (String cameraId : cameraIdList) {
        CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
        int cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
        int[] cameraCapabilities =
            characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API
        boolean isBackwardCompatible = false;
        for (int capability : cameraCapabilities) {
            if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
                isBackwardCompatible = true;
                break;
            }
        }
        ...
    }
} catch (CameraAccessException e) {
    Log.e(TAG, e.getMessage());
    ...
}

متغیر 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) }
    }
}

جاوا

public String getFirstCameraIdFacing(CameraManager cameraManager, @Nullable Integer facing) {
    if (facing == null) facing = CameraMetadata.LENS_FACING_BACK;
    String cameraId = null;

    try {
        // Get a list of all compatible cameras
        String[] cameraIdList = cameraManager.getCameraIdList();

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration and backward compatibility
        for (String id : cameraIdList) {
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
            int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
            for (int capability : capabilities) {
                if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
                        && characteristics.get(CameraCharacteristics.LENS_FACING).equals(facing)) {
                    cameraId = id;
                    break;
                }
            }
        }

        // If no camera matches the desired orientation, return the first one from the list
        cameraId = cameraIdList[0];
    } catch (CameraAccessException e) {
        Log.e(TAG, "getFirstCameraIdFacing: " + e.getMessage());
    }

    return cameraId;
}

فعال کردن سوئیچ دوربین

بسیاری از برنامه های دوربین به کاربران این امکان را می دهند که بین دوربین ها جابجا شوند:

شکل 1. دکمه دوربین را در برنامه دوربین 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)
    }
}

جاوا

public List<String> filterCompatibleCameras(CameraManager cameraManager, String[] cameraIds) {
    final List<String> compatibleCameras = new ArrayList<>();

    try {
        for (String id : cameraIds) {
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
            int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
            for (int capability : capabilities) {
                if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
                    compatibleCameras.add(id);
                }
            }
        }
    } catch (CameraAccessException e) {
        Log.e(TAG, "filterCompatibleCameras: " + e.getMessage());
    }

    return compatibleCameras;
}

public List<String> filterCameraIdsFacing(CameraManager cameraManager, List<String> cameraIds, int lensFacing) {
    final List<String> compatibleCameras = new ArrayList<>();

    try {
        for (String id : cameraIds) {
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == lensFacing) {
                compatibleCameras.add(id);
            }
        }
    } catch (CameraAccessException e) {
        Log.e(TAG, "filterCameraIdsFacing: " + e.getMessage());
    }

    return compatibleCameras;
}

public String getNextCameraId(CameraManager cameraManager, @Nullable String currentCameraId) {
    String nextCameraId = null;

    try {
        // Get all front, back, and external cameras in 3 separate lists
        List<String> compatibleCameraIds = filterCompatibleCameras(cameraManager, cameraManager.getCameraIdList());
        List<String> backCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_BACK);
        List<String> frontCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_FRONT);
        List<String>externalCameras = filterCameraIdsFacing(cameraManager, compatibleCameraIds, CameraMetadata.LENS_FACING_EXTERNAL);

        // The recommended order of iteration is: all external, first back, first front
        List<String> allCameras = new ArrayList<>(externalCameras);
        if (!backCameras.isEmpty()) allCameras.add(backCameras.get(0));
        if (!frontCameras.isEmpty()) allCameras.add(frontCameras.get(0));

        // Get the index of the currently selected camera in the list
        int cameraIndex = allCameras.indexOf(currentCameraId);

        // The selected camera may not be in the list, for example it could be an
        // external camera that has been removed by the user
        if (cameraIndex == -1) {
            // Return the first camera from the list
            nextCameraId = !allCameras.isEmpty() ? allCameras.get(0) : null;
        else {
            if (!allCameras.isEmpty()) {
                // Return the next camera from the list, wrap around if necessary
                nextCameraId = allCameras.get((cameraIndex + 1) % allCameras.size());
            }
        }
    } catch (CameraAccessException e) {
        Log.e(TAG, "getNextCameraId: " + e.getMessage());
    }

    return nextCameraId;
}

این کد برای مجموعه بزرگی از دستگاه ها با پیکربندی های مختلف کار می کند. برای اطلاعات بیشتر در مورد حسابداری موارد لبه، CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA را ببینید.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

برنامه های سازگار ایجاد کنید

برای برنامه‌هایی که هنوز از Camera API منسوخ شده استفاده می‌کنند، تعداد دوربین‌هایی که Camera.getNumberOfCameras() برمی‌گرداند به اجرای OEM بستگی دارد. اگر یک دوربین چندگانه منطقی در سیستم وجود داشته باشد، برای حفظ سازگاری برنامه به عقب، این روش تنها یک دوربین را برای هر دوربین منطقی و گروه دوربین های فیزیکی زیربنایی نشان می دهد. از Camera2 API برای دیدن همه دوربین ها استفاده کنید.

برای اطلاعات پس زمینه بیشتر در مورد جهت گیری دوربین، Camera.CameraInfo.orientation را ببینید.

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

انواع دستگاه ها را در خود جای دهد

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