Mengontrol kamera

Dalam tutorial ini, kita akan membahas cara mengontrol hardware kamera secara langsung menggunakan API framework.

Catatan: Halaman ini merujuk ke class Camera, yang sudah tidak digunakan lagi. Sebaiknya gunakan CameraX atau, untuk kasus penggunaan tertentu, gunakan Camera2. CameraX dan Camera2 mendukung Android 5.0 (API level 21) dan versi yang lebih baru.

Mengontrol kamera perangkat secara langsung memerlukan lebih banyak kode daripada meminta gambar atau video dari aplikasi kamera yang ada. Namun, jika Anda ingin membuat aplikasi kamera khusus atau sesuatu yang terintegrasi sepenuhnya di UI aplikasi, pelajaran ini akan menunjukkan caranya.

Lihat referensi terkait berikut ini:

Membuka Objek Kamera

Mendapatkan instance objek Camera adalah langkah pertama dalam proses mengontrol kamera secara langsung. Seperti yang dilakukan oleh aplikasi Kamera Android, cara yang direkomendasikan untuk mengakses kamera adalah dengan membuka Camera di thread terpisah yang diluncurkan dari onCreate(). Ini merupakan pendekatan yang bagus karena prosesnya dapat memerlukan waktu agak lama dan dapat mengacaukan UI thread. Dalam implementasi yang lebih mendasar, operasi membuka kamera dapat dialihkan ke metode onResume() untuk memfasilitasi penggunaan ulang kode dan menyederhanakan alur kontrol.

Memanggil Camera.open() akan memunculkan pengecualian jika kamera sudah digunakan oleh aplikasi lain, jadi kami menggabungkannya dalam blok try.

Kotlin

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

Java

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

Sejak API level 9, framework kamera mendukung multi-kamera. Jika Anda menggunakan API lama dan memanggil open() tanpa argumen, Anda akan mendapatkan kamera hadap belakang yang pertama.

Membuat Pratinjau Kamera

Proses pengambilan gambar biasanya mengharuskan pengguna untuk melihat pratinjau subjek sebelum mengklik tombol shutter. Untuk keperluan tersebut, Anda dapat menggunakan SurfaceView untuk menggambar pratinjau dari objek yang ditangkap oleh sensor kamera.

Class Pratinjau

Untuk mulai menampilkan pratinjau, Anda memerlukan class pratinjau. Pratinjau memerlukan penerapan antarmuka android.view.SurfaceHolder.Callback, yang digunakan untuk meneruskan data gambar dari hardware kamera ke aplikasi.

Kotlin

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

Java

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

Class pratinjau harus diteruskan ke objek Camera sebelum pratinjau gambar aktif dapat dimulai, seperti ditunjukkan dalam bagian di bawah.

Menetapkan dan Memulai Pratinjau

Instance kamera dan pratinjau terkaitnya harus dibuat sesuai urutan tertentu, dengan objek kamera sebagai yang pertama. Dalam cuplikan kode di bawah, proses inisialisasi kamera diringkas sedemikian rupa sehingga Camera.startPreview() dipanggil oleh metode setCamera() setiap kali pengguna melakukan sesuatu untuk mengubah kamera. Pratinjau juga harus dimulai ulang dalam metode callback surfaceChanged() class pratinjau.

Kotlin

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

Java

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

Memodifikasi Setelan Kamera

Setelan kamera mengubah cara kamera mengambil gambar, dari tingkat zoom hingga exposure compensation. Contoh ini hanya mengubah ukuran pratinjau. Lihat kode sumber aplikasi Kamera untuk informasi yang lebih lengkap lagi.

Kotlin

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

Java

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

Menetapkan Orientasi Pratinjau

Sebagian besar aplikasi kamera mengunci layar ke mode lanskap karena mode tersebut merupakan orientasi yang natural untuk sensor kamera. Setelan ini tidak mencegah Anda mengambil foto dalam mode potret, karena orientasi perangkat direkam dalam header EXIF. Metode setCameraDisplayOrientation() memungkinkan Anda mengubah tampilan pratinjau tanpa memengaruhi cara gambar direkam. Namun, pada Android sebelum API level 14, Anda harus menghentikan pratinjau sebelum mengubah orientasi, lalu memulainya ulang.

Mengambil Gambar

Gunakan metode Camera.takePicture() untuk mengambil gambar setelah pratinjau dimulai. Anda dapat membuat objek Camera.PictureCallback dan Camera.ShutterCallback lalu meneruskannya ke Camera.takePicture().

Jika ingin mengambil gambar secara kontinu, Anda dapat membuat Camera.PreviewCallback yang menerapkan onPreviewFrame(). Untuk sesuatu di antaranya, Anda hanya dapat mengambil frame pratinjau yang dipilih, atau menyiapkan tindakan tertunda untuk memanggil takePicture().

Memulai Ulang Pratinjau

Setelah gambar diambil, Anda harus memulai ulang pratinjau agar pengguna dapat mengambil gambar lain. Dalam contoh ini, proses mulai ulang dilakukan dengan meng-overload tombol shutter.

Kotlin

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

Java

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

Menghentikan Pratinjau dan Melepas Kamera

Setelah aplikasi Anda selesai menggunakan kamera, saatnya untuk membersihkan. Secara khusus, Anda harus melepas objek Camera; jika tidak, Anda berisiko menimbulkan error pada aplikasi lain, termasuk instance baru aplikasi Anda sendiri.

Kapan sebaiknya Anda menghentikan pratinjau dan melepas kamera? Menghilangnya permukaan pratinjau adalah indikasi yang tepat bahwa sudah saatnya untuk menghentikan pratinjau dan melepas kamera, seperti yang ditunjukkan dalam metode ini dari class Preview.

Kotlin

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

Java

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

Seperti disinggung di atas, prosedur ini juga merupakan bagian dari metode setCamera(), jadi inisialisasi kamera selalu dimulai dengan menghentikan pratinjau.