Controllare la videocamera

In questa lezione parleremo di come controllare direttamente l'hardware della videocamera le API del framework.

Nota:questa pagina fa riferimento alla classe Fotocamera, che è stata ritirata. Ti consigliamo di utilizzare CameraX o, per casi d'uso specifici, Camera2. Sia CameraX che Camera2 supportano Android 5.0 (livello API 21) e versioni successive.

Il controllo diretto della fotocamera di un dispositivo richiede molto più codice rispetto alla richiesta di immagini o video da applicazioni di fotocamera esistenti. Se però vuoi creare un'applicazione per la videocamera specializzata o qualcosa di completamente integrato nell'interfaccia utente dell'app, questa lezione ti mostra come fare.

Consulta le seguenti risorse correlate:

Apri l'oggetto Fotocamera

Ottieni un'istanza dell'oggetto Camera è il primo passaggio nella per controllare direttamente la videocamera. Così come fa l'applicazione Fotocamera di Android, il modo consigliato per accedere alla fotocamera è aprire Camera in un thread separato lanciato da onCreate(). Questo approccio è una buona idea poiché può richiedere del tempo e potrebbe bloccare il thread dell'interfaccia utente. In un'implementazione più basilare, l'apertura della fotocamera può essere differita al metodo onResume() per facilitare il riutilizzo del codice e mantenere il flusso controllo semplice.

La chiamata a Camera.open() genera un un'altra eccezione se la videocamera è già utilizzata da un'altra applicazione, quindi in un blocco 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;
    }
}

A partire dal livello API 9, il framework della fotocamera supporta più videocamere. Se utilizzi API legacy e chiama open() senza un argomento, si ottiene la prima fotocamera posteriore.

Crea l'anteprima della fotocamera

Per scattare una foto, in genere gli utenti devono visualizzare un'anteprima del soggetto prima di fare clic il pulsante di scatto. Per farlo, puoi utilizzare un SurfaceView per disegnare anteprime di ciò che il sensore della videocamera sta rilevando la situazione.

Anteprima corso

Per iniziare a visualizzare un'anteprima, hai bisogno del corso Anteprima. La richiede l'implementazione dell'interfaccia android.view.SurfaceHolder.Callback, che viene utilizzata per trasmettere immagini dall'hardware della videocamera all'applicazione.

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

La classe di anteprima deve essere passata all'oggetto Camera prima della pubblicazione l'anteprima dell'immagine, come illustrato nella sezione successiva.

Imposta e avvia l'anteprima

Un'istanza della videocamera e la relativa anteprima devono essere create in un ambiente in cui l'oggetto fotocamera è il primo. Nello snippet seguente, il processo di inizializzazione della fotocamera viene incapsulato in modo che Camera.startPreview() venga chiamato setCamera(), ogni volta che l'utente fa qualcosa per modificare fotocamera. L'anteprima deve essere riavviata anche nel metodo di callback della classe di anteprima surfaceChanged().

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

Modifica le impostazioni della videocamera

Le impostazioni della fotocamera modificano il modo in cui la fotocamera scatta le foto, dallo zoom livello alla compensazione dell'esposizione. Questo esempio modifica solo le dimensioni dell'anteprima, vedere il codice sorgente dell'applicazione Fotocamera per molte altre.

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

Imposta l'orientamento dell'anteprima

La maggior parte delle applicazioni della fotocamera blocca il display in modalità Orizzontale, l'orientamento del sensore della fotocamera. Questa impostazione non impedisce di utilizzare la modalità Ritratto foto, perché l'orientamento del dispositivo è registrato nell'intestazione EXIF. Il metodo setCameraDisplayOrientation() ti consente di modificare come viene visualizzata l'anteprima senza influire sulla registrazione dell'immagine. Tuttavia, nelle versioni precedenti di Android al livello API 14, devi interrompere l'anteprima prima di modificare l'orientamento e riavviarla.

Scatta una foto

Usa Camera.takePicture() per scattare una foto dopo l'avvio dell'anteprima. Puoi creare oggetti Camera.PictureCallback e Camera.ShutterCallback e trasmetterli a Camera.takePicture().

Se vuoi acquisire immagini in modo continuo, puoi creare un elemento Camera.PreviewCallback che implementa onPreviewFrame(). Per a una via di mezzo, puoi acquisire solo determinati fotogrammi di anteprima oppure configurare azione ritardata per chiamare takePicture().

Riavviare l'anteprima

Dopo aver scattato una foto, devi riavviare l'anteprima prima che l'utente può scattare un'altra foto. In questo esempio, il riavvio viene eseguito con un sovraccarico il pulsante di scatto.

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

Interrompi l'anteprima e rilascia la fotocamera

Una volta che l'applicazione ha finito di utilizzare la fotocamera, è il momento di eseguire la pulizia. Nella specifica, devi rilasciare l'oggetto Camera per non provocare l'arresto anomalo di altri delle tue applicazioni, incluse nuove istanze della tua applicazione.

Quando devi interrompere l'anteprima e rilasciare la fotocamera? Il fatto che la tua l'eliminazione della superficie di anteprima è un ottimo indizio che è il momento visualizza l'anteprima e rilascia la fotocamera, come mostrato in questi metodi dalla classe 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;
    }
}

All'inizio della lezione, questa procedura faceva parte del metodo setCamera(), quindi l'inizializzazione di una videocamera inizia sempre con l'interruzione l'anteprima.