ক্যামেরা নিয়ন্ত্রণ করুন

এই পাঠে আমরা ফ্রেমওয়ার্ক এপিআই ব্যবহার করে সরাসরি ক্যামেরা হার্ডওয়্যার নিয়ন্ত্রণ করার পদ্ধতি নিয়ে আলোচনা করব।

দ্রষ্টব্য: এই পৃষ্ঠাটি Camera ক্লাসকে নির্দেশ করে, যা এখন আর ব্যবহৃত হয় না। আমরা CameraX অথবা, বিশেষ কিছু ক্ষেত্রে, Camera2 ব্যবহার করার পরামর্শ দিই। CameraX এবং Camera2 উভয়ই Android 5.0 (API লেভেল 21) এবং তার পরবর্তী সংস্করণ সমর্থন করে।

বিদ্যমান ক্যামেরা অ্যাপ্লিকেশনগুলো থেকে ছবি বা ভিডিও অনুরোধ করার চেয়ে সরাসরি কোনো ডিভাইসের ক্যামেরা নিয়ন্ত্রণ করতে অনেক বেশি কোডের প্রয়োজন হয়। তবে, আপনি যদি একটি বিশেষায়িত ক্যামেরা অ্যাপ্লিকেশন তৈরি করতে চান অথবা আপনার অ্যাপের UI-এর সাথে সম্পূর্ণরূপে সমন্বিত কিছু করতে চান, তাহলে এই পাঠে তার উপায় দেখানো হয়েছে।

নিম্নলিখিত সম্পর্কিত উৎসসমূহ দেখুন:

ক্যামেরা অবজেক্টটি খুলুন

ক্যামেরা সরাসরি নিয়ন্ত্রণ করার প্রক্রিয়ার প্রথম ধাপ হলো Camera অবজেক্টের একটি ইনস্ট্যান্স পাওয়া। অ্যান্ড্রয়েডের নিজস্ব ক্যামেরা অ্যাপ্লিকেশনের মতোই, ক্যামেরা অ্যাক্সেস করার প্রস্তাবিত উপায় হলো onCreate() থেকে চালু করা একটি পৃথক থ্রেডে Camera খোলা। এই পদ্ধতিটি একটি ভালো উপায়, কারণ এতে কিছুটা সময় লাগতে পারে এবং UI থ্রেডকে ধীর করে দিতে পারে। আরও একটি সাধারণ বাস্তবায়নে, কোড পুনঃব্যবহার সহজ করতে এবং নিয়ন্ত্রণের প্রবাহকে সরল রাখতে ক্যামেরা খোলার কাজটি onResume() মেথডের জন্য স্থগিত রাখা যেতে পারে।

ক্যামেরাটি যদি অন্য কোনো অ্যাপ্লিকেশন দ্বারা আগে থেকেই ব্যবহৃত হয়, তাহলে Camera.open() কল করলে একটি এক্সেপশন থ্রো হয়, তাই আমরা এটিকে একটি try ব্লকের মধ্যে রাখি।

কোটলিন

private fun safeCameraOpen(id: Int): Boolean {
    return try {
        releaseCameraAndPreview()
        mCamera = Camera.open(id)
        true
    } catch (e: Exception) {
        Log.e(getString(R.string.app_name), "failed to open Camera")
        e.printStackTrace()
        false
    }
}

private fun releaseCameraAndPreview() {
    preview?.setCamera(null)
    mCamera?.also { camera ->
        camera.release()
        mCamera = null
    }
}

জাভা

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;

    try {
        releaseCameraAndPreview();
        camera = Camera.open(id);
        qOpened = (camera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;
}

private void releaseCameraAndPreview() {
    preview.setCamera(null);
    if (camera != null) {
        camera.release();
        camera = null;
    }
}

এপিআই লেভেল ৯ থেকে ক্যামেরা ফ্রেমওয়ার্ক একাধিক ক্যামেরা সমর্থন করে। আপনি যদি লিগ্যাসি এপিআই ব্যবহার করে কোনো আর্গুমেন্ট ছাড়া open() কল করেন, তাহলে আপনি প্রথম পেছনের ক্যামেরাটি পাবেন।

ক্যামেরা প্রিভিউ তৈরি করুন

ছবি তোলার জন্য সাধারণত শাটার চাপার আগে ব্যবহারকারীদের তাদের সাবজেক্টের একটি প্রিভিউ দেখার প্রয়োজন হয়। এটি করার জন্য, ক্যামেরা সেন্সর যা ধারণ করছে তার প্রিভিউ আঁকতে আপনি একটি SurfaceView ব্যবহার করতে পারেন।

প্রিভিউ ক্লাস

প্রিভিউ দেখানো শুরু করতে আপনার প্রিভিউ ক্লাসটি প্রয়োজন। প্রিভিউয়ের জন্য android.view.SurfaceHolder.Callback ইন্টারফেসটির ইমপ্লিমেন্টেশন প্রয়োজন, যা ক্যামেরা হার্ডওয়্যার থেকে অ্যাপ্লিকেশনে ছবির ডেটা পাঠানোর জন্য ব্যবহৃত হয়।

কোটলিন

class Preview(
        context: Context,
        val surfaceView: SurfaceView = SurfaceView(context)
) : ViewGroup(context), SurfaceHolder.Callback {

    var mHolder: SurfaceHolder = surfaceView.holder.apply {
        addCallback(this@Preview)
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }
    ...
}

জাভা

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView surfaceView;
    SurfaceHolder holder;

    Preview(Context context) {
        super(context);

        surfaceView = new SurfaceView(context);
        addView(surfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        holder = surfaceView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}

পরবর্তী বিভাগে দেখানো অনুযায়ী, লাইভ ইমেজ প্রিভিউ শুরু করার আগে প্রিভিউ ক্লাসটি Camera অবজেক্টে পাস করতে হবে।

প্রিভিউ সেট করুন এবং শুরু করুন

একটি ক্যামেরা ইনস্ট্যান্স এবং এর সম্পর্কিত প্রিভিউ একটি নির্দিষ্ট ক্রমে তৈরি করতে হবে, যেখানে ক্যামেরা অবজেক্টটি প্রথমে থাকবে। নিম্নলিখিত কোড স্নিপেটে, ক্যামেরা ইনিশিয়ালাইজ করার প্রক্রিয়াটি এনক্যাপসুলেট করা হয়েছে, যাতে ব্যবহারকারী যখনই ক্যামেরা পরিবর্তন করার জন্য কিছু করেন, তখনই setCamera() মেথডটি Camera.startPreview() কল করে। প্রিভিউ ক্লাসের surfaceChanged() কলব্যাক মেথডেও প্রিভিউটি পুনরায় চালু করতে হবে।

কোটলিন

fun setCamera(camera: Camera?) {
    if (mCamera == camera) {
        return
    }

    stopPreviewAndFreeCamera()

    mCamera = camera

    mCamera?.apply {
        mSupportedPreviewSizes = parameters.supportedPreviewSizes
        requestLayout()

        try {
            setPreviewDisplay(holder)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        startPreview()
    }
}

জাভা

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }

    stopPreviewAndFreeCamera();

    mCamera = camera;

    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        supportedPreviewSizes = localSizes;
        requestLayout();

        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        mCamera.startPreview();
    }
}

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

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

কোটলিন

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    mCamera?.apply {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        parameters?.also { params ->
            params.setPreviewSize(previewSize.width, previewSize.height)
            requestLayout()
            parameters = params
        }

        // Important: Call startPreview() to start updating the preview surface.
        // Preview must be started before you can take a picture.
        startPreview()
    }
}

জাভা

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(previewSize.width, previewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);

    // Important: Call startPreview() to start updating the preview surface.
    // Preview must be started before you can take a picture.
    mCamera.startPreview();
}

প্রিভিউ ওরিয়েন্টেশন সেট করুন

বেশিরভাগ ক্যামেরা অ্যাপ্লিকেশন ডিসপ্লেকে ল্যান্ডস্কেপ মোডে লক করে রাখে, কারণ এটিই ক্যামেরা সেন্সরের স্বাভাবিক ওরিয়েন্টেশন। এই সেটিংটি আপনাকে পোর্ট্রেট-মোডে ছবি তুলতে বাধা দেয় না, কারণ ডিভাইসের ওরিয়েন্টেশন EXIF ​​হেডারে রেকর্ড করা থাকে। setCameraDisplayOrientation() মেথডটি আপনাকে ছবিটি কীভাবে রেকর্ড হবে তার উপর কোনো প্রভাব না ফেলেই প্রিভিউ কীভাবে প্রদর্শিত হবে তা পরিবর্তন করার সুযোগ দেয়। তবে, API লেভেল 14-এর আগের অ্যান্ড্রয়েড সংস্করণগুলিতে, ওরিয়েন্টেশন পরিবর্তন করার আগে আপনাকে অবশ্যই প্রিভিউ বন্ধ করতে হবে এবং তারপর আবার চালু করতে হবে।

একটি ছবি তুলুন

প্রিভিউ শুরু হয়ে গেলে ছবি তোলার জন্য Camera.takePicture() মেথডটি ব্যবহার করুন। আপনি Camera.PictureCallback এবং Camera.ShutterCallback অবজেক্ট তৈরি করে সেগুলোকে Camera.takePicture() -এ পাস করতে পারেন।

আপনি যদি একটানা ছবি তুলতে চান, তাহলে onPreviewFrame() ইমপ্লিমেন্ট করে একটি Camera.PreviewCallback তৈরি করতে পারেন। এর মাঝামাঝি কোনো কিছুর জন্য, আপনি শুধু নির্বাচিত প্রিভিউ ফ্রেমগুলো ক্যাপচার করতে পারেন, অথবা takePicture() কল করার জন্য একটি বিলম্বিত অ্যাকশন সেট আপ করতে পারেন।

প্রিভিউ পুনরায় চালু করুন

একটি ছবি তোলার পর, ব্যবহারকারী আরেকটি ছবি তোলার আগে আপনাকে অবশ্যই প্রিভিউটি পুনরায় চালু করতে হবে। এই উদাহরণে, শাটার বাটনটি ওভারলোড করার মাধ্যমে পুনরায় চালু করা হয়।

কোটলিন

fun onClick(v: View) {
    previewState = if (previewState == K_STATE_FROZEN) {
        camera?.startPreview()
        K_STATE_PREVIEW
    } else {
        camera?.takePicture(null, rawCallback, null)
        K_STATE_BUSY
    }
    shutterBtnConfig()
}

জাভা

@Override
public void onClick(View v) {
    switch(previewState) {
    case K_STATE_FROZEN:
        camera.startPreview();
        previewState = K_STATE_PREVIEW;
        break;

    default:
        camera.takePicture( null, rawCallback, null);
        previewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}

প্রিভিউ বন্ধ করুন এবং ক্যামেরা ছেড়ে দিন।

আপনার অ্যাপ্লিকেশনটির ক্যামেরা ব্যবহার করা শেষ হয়ে গেলে, এবার সবকিছু গুছিয়ে ফেলার পালা। বিশেষ করে, আপনাকে অবশ্যই Camera অবজেক্টটি রিলিজ করতে হবে, অন্যথায় আপনার নিজের অ্যাপ্লিকেশনের নতুন ইনস্ট্যান্স সহ অন্যান্য অ্যাপ্লিকেশন ক্র্যাশ করার ঝুঁকি থাকবে।

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

কোটলিন

override fun surfaceDestroyed(holder: SurfaceHolder) {
    // Surface will be destroyed when we return, so stop the preview.
    // Call stopPreview() to stop updating the preview surface.
    mCamera?.stopPreview()
}

/**
 * When this function returns, mCamera will be null.
 */
private fun stopPreviewAndFreeCamera() {
    mCamera?.apply {
        // Call stopPreview() to stop updating the preview surface.
        stopPreview()

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        release()

        mCamera = null
    }
}

জাভা

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();
    }
}

/**
 * When this function returns, mCamera will be null.
 */
private void stopPreviewAndFreeCamera() {

    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        mCamera.release();

        mCamera = null;
    }
}

পাঠের শুরুতে, এই পদ্ধতিটি setCamera() মেথডেরও একটি অংশ ছিল, তাই ক্যামেরা চালু করার কাজটি সবসময় প্রিভিউ বন্ধ করার মাধ্যমে শুরু হয়।