ক্যামেরা লেন্স এবং ক্ষমতা

দ্রষ্টব্য: এই পৃষ্ঠাটি Camera2 প্যাকেজ সম্পর্কিত। যদি না আপনার অ্যাপের Camera2-এর নির্দিষ্ট, নিম্ন-স্তরের বৈশিষ্ট্যগুলির প্রয়োজন হয়, আমরা CameraX ব্যবহার করার পরামর্শ দিই। CameraX এবং Camera2 উভয়ই Android 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;
}

ক্যামেরা পরিবর্তন সক্ষম করুন

অনেক ক্যামেরা অ্যাপ ব্যবহারকারীদের ক্যামেরা পরিবর্তন করার সুযোগ দেয়:

চিত্র ১. গুগল ক্যামেরা অ্যাপে ক্যামেরা পরিবর্তন করার বাটন।

অনেক ডিভাইসে একই দিকে মুখ করা একাধিক ক্যামেরা থাকে। এমনকি কোনো কোনোটিতে এক্সটার্নাল ইউএসবি ক্যামেরাও থাকে। ব্যবহারকারীদের বিভিন্ন দিকে মুখ করা ক্যামেরার মধ্যে পরিবর্তন করার সুবিধা দিতে, প্রতিটি সম্ভাব্য লেন্স-মুখী কনফিগারেশনের জন্য প্রথম উপলব্ধ ক্যামেরাটি বেছে নিন।

পরবর্তী ক্যামেরা নির্বাচন করার কোনো সর্বজনীন যুক্তি না থাকলেও, নিম্নলিখিত কোডটি বেশিরভাগ ক্ষেত্রেই কাজ করে:

কোটলিন

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 দেখুন।

সামঞ্জস্যপূর্ণ অ্যাপ তৈরি করুন

যেসব অ্যাপ এখনও অপ্রচলিত ক্যামেরা এপিআই ব্যবহার করছে, তাদের ক্ষেত্রে Camera.getNumberOfCameras() যে সংখ্যক ক্যামেরা রিটার্ন করে তা OEM ইমপ্লিমেন্টেশনের উপর নির্ভর করে। যদি সিস্টেমে লজিক্যাল মাল্টি-ক্যামেরা থাকে, অ্যাপের ব্যাকওয়ার্ড কম্প্যাটিবিলিটি বজায় রাখার জন্য, এই মেথডটি প্রতিটি লজিক্যাল ক্যামেরা এবং তার অন্তর্নিহিত ফিজিক্যাল ক্যামেরা গ্রুপের জন্য শুধুমাত্র একটি ক্যামেরা দেখাবে। সমস্ত ক্যামেরা দেখতে Camera2 এপিআই ব্যবহার করুন।

ক্যামেরার অভিমুখ সম্পর্কে আরও বিস্তারিত তথ্যের জন্য Camera.CameraInfo.orientation দেখুন।

সাধারণত, ক্যামেরার সমস্ত orientation জানতে Camera.getCameraInfo() API ব্যবহার করুন, এবং যেসব ব্যবহারকারী ক্যামেরা পরিবর্তন করছেন, তাদের জন্য প্রতিটি উপলব্ধ ওরিয়েন্টেশনের কেবল একটি ক্যামেরাই প্রদর্শন করুন।

সব ধরনের ডিভাইসের জন্য উপযুক্ত

এটা ধরে নেবেন না যে আপনার অ্যাপটি সবসময় এক বা দুটি ক্যামেরাযুক্ত হ্যান্ডহেল্ড ডিভাইসে চলবে। এর পরিবর্তে, অ্যাপটির জন্য সবচেয়ে উপযুক্ত ক্যামেরাগুলো বেছে নিন। যদি আপনার কোনো নির্দিষ্ট ক্যামেরার প্রয়োজন না হয়, তবে কাঙ্ক্ষিত দিকে মুখ করা প্রথম ক্যামেরাটিই নির্বাচন করুন। যদি কোনো নির্দিষ্ট দিকে মুখ করা ক্যামেরাটি উপলব্ধ না থাকে, তবে বিবেচনা করুন যে ব্যবহারকারী অন্য কোনো ক্যামেরা দিয়ে তার যাত্রা সম্পন্ন করতে পারবেন কিনা। ক্যামেরা হার্ডওয়্যারের উপর ভিত্তি করে নির্দিষ্ট কিছু ডিভাইসে আপনার অ্যাপের প্রাপ্যতা সীমাবদ্ধ করবেন না। যদি একটি এক্সটার্নাল ক্যামেরা সংযুক্ত থাকে, তবে ব্যবহারকারী সম্ভবত সেটিকে ডিফল্ট হিসেবেই পছন্দ করেন।