कैमरे को कंट्रोल करना

इस लेसन में, हम फ़्रेमवर्क एपीआई का इस्तेमाल करके, सीधे कैमरे के हार्डवेयर को कंट्रोल करने का तरीका बताते हैं.

ध्यान दें: इस पेज पर, Camera क्लास के बारे में बताया गया है. हालांकि, अब इस क्लास का इस्तेमाल नहीं किया जा सकता. हमारा सुझाव है कि आप CameraX का इस्तेमाल करें. इसके अलावा, इस्तेमाल के कुछ खास उदाहरणों के लिए, Camera2 का इस्तेमाल करें. CameraX और Camera2, दोनों ही Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन पर काम करते हैं.

किसी डिवाइस के कैमरे को सीधे तौर पर कंट्रोल करने के लिए, मौजूदा कैमरा ऐप्लिकेशन से फ़ोटो या वीडियो का अनुरोध करने के मुकाबले ज़्यादा कोड की ज़रूरत होती है. हालांकि, अगर आपको कोई खास कैमरा ऐप्लिकेशन बनाना है या अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में पूरी तरह से इंटिग्रेट किया गया कोई ऐप्लिकेशन बनाना है, तो इस लेसन में बताया गया तरीका अपनाएं.

इस बारे में ज़्यादा जानने के लिए, यहां दिए गए लेख पढ़ें:

कैमरा ऑब्जेक्ट खोलना

कैमरे को सीधे तौर पर कंट्रोल करने की प्रोसेस का पहला चरण, Camera ऑब्जेक्ट का इंस्टेंस पाना है. Android के कैमरा ऐप्लिकेशन की तरह ही, कैमरे को ऐक्सेस करने का सुझाया गया तरीका यह है कि onCreate() से लॉन्च की गई अलग थ्रेड पर Camera खोला जाए. यह तरीका अपनाना अच्छा है, क्योंकि इसमें कुछ समय लग सकता है और यूज़र इंटरफ़ेस (यूआई) थ्रेड धीमा हो सकता है. कोड को फिर से इस्तेमाल करने और कंट्रोल के फ़्लो को आसान बनाए रखने के लिए, कैमरे को खोलने की प्रोसेस को onResume() तरीके पर बाद में किया जा सकता है.

अगर कोई दूसरा ऐप्लिकेशन पहले से ही कैमरे का इस्तेमाल कर रहा है, तो Camera.open() को कॉल करने पर अपवाद दिखता है. इसलिए, हम इसे try ब्लॉक में रैप करते हैं.

KotlinJava
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;
    }
}

एपीआई लेवल 9 से, कैमरा फ़्रेमवर्क में एक से ज़्यादा कैमरे इस्तेमाल करने की सुविधा है. अगर लेगसी एपीआई का इस्तेमाल किया जाता है और open() को बिना किसी आर्ग्युमेंट के कॉल किया जाता है, तो आपको पीछे वाला पहला कैमरा मिलता है.

कैमरे की झलक बनाना

आम तौर पर, फ़ोटो लेने के लिए ज़रूरी है कि उपयोगकर्ता शटर पर क्लिक करने से पहले, अपने सब्जेक्ट की झलक देख पाएं. इसके लिए, SurfaceView का इस्तेमाल करके, कैमरा सेंसर से कैप्चर की गई चीज़ों की झलक देखी जा सकती है.

झलक वाली क्लास

झलक दिखाने की सुविधा का इस्तेमाल शुरू करने के लिए, आपके पास झलक वाली क्लास होनी चाहिए. झलक देखने के लिए, android.view.SurfaceHolder.Callback इंटरफ़ेस को लागू करना ज़रूरी है. इसका इस्तेमाल, कैमरे के हार्डवेयर से ऐप्लिकेशन में इमेज का डेटा भेजने के लिए किया जाता है.

KotlinJava
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 ऑब्जेक्ट में पास करना ज़रूरी है. इस बारे में अगले सेक्शन में बताया गया है.

झलक सेट करना और उसे शुरू करना

कैमरा इंस्टेंस और उससे जुड़ी झलक को एक खास क्रम में बनाया जाना चाहिए. इसमें कैमरा ऑब्जेक्ट पहले होना चाहिए. नीचे दिए गए स्निपेट में, कैमरे को शुरू करने की प्रोसेस को कैप्सुलेट किया गया है, ताकि जब भी उपयोगकर्ता कैमरे को बदलने के लिए कुछ करे, तो Camera.startPreview() को setCamera() तरीके से कॉल किया जाए. झलक देखने की क्लास surfaceChanged() कॉलबैक के तरीके में भी झलक को फिर से शुरू करना होगा.

KotlinJava
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();
    }
}

कैमरे की सेटिंग में बदलाव करना

कैमरे की सेटिंग से, कैमरे की फ़ोटो लेने के तरीके में बदलाव होता है. जैसे, ज़ूम लेवल से लेकर एक्सपोज़र कंपेसेशन तक. इस उदाहरण में सिर्फ़ झलक का साइज़ बदला गया है. ज़्यादा जानकारी के लिए, Camera ऐप्लिकेशन का सोर्स कोड देखें.

KotlinJava
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() तरीके की मदद से, इमेज को रिकॉर्ड करने के तरीके पर असर डाले बिना, झलक दिखाने का तरीका बदला जा सकता है. हालांकि, एपीआई लेवल 14 से पहले के Android वर्शन में, ओरिएंटेशन बदलने से पहले आपको झलक दिखाने की सुविधा बंद करनी होगी. इसके बाद, उसे फिर से चालू करना होगा.

फ़ोटो खींचना

झलक देखने की सुविधा शुरू होने के बाद, Camera.takePicture() तरीका इस्तेमाल करके फ़ोटो लें. Camera.PictureCallback और Camera.ShutterCallback ऑब्जेक्ट बनाए जा सकते हैं और उन्हें Camera.takePicture() में पास किया जा सकता है.

अगर आपको लगातार इमेज लेनी हैं, तो Camera.PreviewCallback बनाएं, जो onPreviewFrame() लागू करता हो. अगर आपको इन दोनों के बीच का कोई विकल्प चाहिए, तो सिर्फ़ चुनिंदा झलक फ़्रेम कैप्चर किए जा सकते हैं. इसके अलावा, takePicture() को कॉल करने के लिए, देर से होने वाली कार्रवाई को सेट अप किया जा सकता है.

झलक को फिर से शुरू करना

फ़ोटो लेने के बाद, उपयोगकर्ता को अगली फ़ोटो लेने से पहले, आपको झलक को फिर से शुरू करना होगा. इस उदाहरण में, शटर बटन को ओवरलोड करके रीस्टार्ट किया गया है.

KotlinJava
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 क्लास के इन तरीकों में इस बारे में बताया गया है.

KotlinJava
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() तरीके में भी यह प्रोसेस शामिल है. इसलिए, कैमरे को शुरू करने के लिए, झलक देखना हमेशा बंद करना पड़ता है.