API multi-camera

Nota: questa pagina si riferisce al pacchetto Camera2. A meno che la tua app non richieda funzionalità specifiche di basso livello di Camera2, ti consigliamo di usare CameraX. Sia CameraX che Camera2 supportano Android 5.0 (livello API 21) e versioni successive.

La funzionalità multicamera è stata introdotta con Android 9 (livello API 28). Dalla sua uscita, sul mercato vengono lanciati dispositivi che supportano l'API. Molti casi d'uso multicamera sono strettamente legate a una specifica configurazione hardware. In altre parole, non tutti i casi d'uso sono compatibili con tutti i dispositivi,  il che rende la fotocamera multicamera presenta un buon candidato per la funzionalità di Google Play Consegna.

Alcuni casi d'uso tipici includono:

  • Zoom: passaggio da una fotocamera all'altra in base all'area ritagliata o alla focale desiderata lunghezza.
  • Profondità: utilizzo di più fotocamere per creare una mappa di profondità.
  • Bokeh: uso di informazioni sulla profondità dedotti per simulare un corpus restringente simile a una fotocamera DSLR intervallo di messa a fuoco.

La differenza tra fotocamere logiche e fisiche

Comprendere l'API multi-camera richiede di comprendere la differenza tra fotocamere logiche e fisiche. Come riferimento, consideriamo un dispositivo con tre fotocamere posteriori. In questo esempio, ciascuna delle tre fotocamere posteriori considerata una fotocamera fisica. Una videocamera logica è quindi un raggruppamento di due o più di quelle fotocamere fisiche. L'output del comando videocamera può essere uno stream proveniente da una delle fotocamere fisiche sottostanti o uno stream confuso proveniente da più di una videocamera fisica sottostante contemporaneamente. In ogni caso, lo stream viene gestito dall'hardware della videocamera Abstraction Layer (HAL).

Molti produttori di telefoni sviluppano applicazioni proprietarie per le fotocamere, che in genere sono preinstallati sui loro dispositivi. Per utilizzare tutte le funzionalità dell'hardware, possono utilizzare API private o nascoste o ricevere un trattamento speciale l'implementazione del driver a cui le altre applicazioni non hanno accesso. Alcune dispositivi implementano il concetto di telecamere logiche fornendo un flusso confuso fotogrammi da diverse fotocamere fisiche, ma solo per alcuni privilegi diverse applicazioni. Spesso, solo una delle fotocamere fisiche è esposta al il modello di machine learning. La situazione per gli sviluppatori di terze parti precedenti ad Android 9 è illustrato nel seguente diagramma:

Figura 1. In genere, le funzionalità della fotocamera sono disponibili solo per per le applicazioni con privilegi

A partire da Android 9, le API private non sono più consentite nelle app per Android. Con l'inclusione del supporto multicamera nel framework, Android ha consigliamo vivamente ai produttori di telefoni di esporre una fotocamera logica di tutte le fotocamere fisiche rivolte nella stessa direzione. Di seguito viene illustrato cosa gli sviluppatori di terze parti dovrebbero aspettarsi di trovare dispositivi con Android 9 e superiore:

Figura 2. Accesso sviluppatore completo a tutti i dispositivi fotocamera a partire da Android 9

Ciò che fornisce la fotocamera logica dipende interamente dall'implementazione dell'OEM di Camera HAL. Ad esempio, un dispositivo come Pixel 3 implementa la sua logica videocamera in modo tale da scegliere una delle proprie fotocamere fisiche in base alla la lunghezza focale e l'area di ritaglio richieste.

API multi-camera

La nuova API aggiunge le nuove costanti, classi e metodi seguenti:

A causa delle modifiche al Compatibility Definition Document (CDD) di Android, la l'API multi-camera soddisfa anche alcune aspettative degli sviluppatori. Dispositivi con doppia fotocamera esisteva prima di Android 9, ma l'apertura di più di una fotocamera contemporaneamente tentativi ed errori. Su Android 9 e versioni successive, modalità multicamera offre un insieme di regole per specificare quando è possibile aprire che fanno parte della stessa videocamera logica.

Nella maggior parte dei casi, i dispositivi con Android 9 e versioni successive espongono tutte le informazioni fisiche (ad eccezione dei sensori meno comuni come gli infrarossi) insieme una fotocamera logica più facile da usare. Per ogni combinazione di stream funzionare, uno stream appartenente a una videocamera logica può essere sostituito due stream provenienti dalle videocamere fisiche sottostanti.

Stream multipli contemporaneamente

Utilizzare più videocamere in streaming contemporaneamente copre le regole per l'utilizzo di più stream contemporaneamente in una singola videocamera. Con un'aggiunta degna di nota, le stesse regole si applicano a più videocamere. CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA spiega come sostituire uno stream logico YUV_420_888 o uno stream non elaborato con due in streaming fisici. Ciò significa che ogni flusso di tipo YUV o RAW può essere sostituito con due flussi di dimensioni e tipo identici. Puoi iniziare con uno stream della videocamera la seguente configurazione garantita per i dispositivi con una sola videocamera:

  • Stream 1: tipo YUV, MAXIMUM dimensione dalla fotocamera logica id = 0

Quindi, un dispositivo con supporto multicamera ti consente di creare una sessione sostituendo lo stream logico YUV con due flussi fisici:

  • Stream 1: tipo YUV, MAXIMUM dimensione dalla videocamera fisica id = 1
  • Stream 2: tipo YUV, MAXIMUM dimensione dalla videocamera fisica id = 2

È possibile sostituire uno stream YUV o RAW con due stream equivalenti se e solo se queste due videocamere fanno parte di un raggruppamento logico delle videocamere, CameraCharacteristics.getPhysicalCameraIds()

Le garanzie fornite dal framework sono solo il minimo indispensabile per ottenere fotogrammi da più di una fotocamera fisica contemporaneamente. Stream aggiuntivi sono supportati nella maggior parte dei dispositivi, a volte consente anche l'apertura di più fotocamere in modo indipendente. Poiché non è una garanzia rigida dal di Google Cloud, la procedura richiede l'esecuzione di test e ottimizzazioni per dispositivo utilizzando per tentativi.

Creazione di una sessione con più videocamere fisiche

Se utilizzi fotocamere fisiche su un dispositivo con più fotocamere, apri un singolo CameraDevice (la fotocamera logica) e interagire con quest'ultima all'interno di una singola durante la sessione. crea la sessione singola utilizzando l'API CameraDevice.createCaptureSession(SessionConfiguration config), che è aggiunta al livello API 28. La configurazione della sessione ha una serie configurazioni, ciascuna delle quali ha un insieme di target di output e, facoltativamente, un l'ID fisico desiderato della fotocamera.

Figura 3. Modello SessionConfiguration e OutputConfiguration

Alle richieste di acquisizione è associata una destinazione di output. Il framework determina la videocamera fisica (o logica) a cui vengono inviate le richieste in base la destinazione di output collegata. Se il target di output corrisponde a uno dei target di output inviati come configurazione di output insieme a una dell'ID videocamera, la videocamera fisica riceve ed elabora la richiesta.

Con un paio di fotocamere fisiche

Un'altra aggiunta alle API per videocamere per la modalità multicamera è la capacità di identificare fotocamere logiche e trovare quelle fisiche alle loro spalle. Puoi definire per identificare le potenziali coppie di fotocamere fisiche che puoi utilizzare per sostituire uno degli stream logici della videocamera:

Kotlin

/**
     * Helper class used to encapsulate a logical camera and two underlying
     * physical cameras
     */
    data class DualCamera(val logicalId: String, val physicalId1: String, val physicalId2: String)

    fun findDualCameras(manager: CameraManager, facing: Int? = null): List {
        val dualCameras = MutableList()

        // Iterate over all the available camera characteristics
        manager.cameraIdList.map {
            Pair(manager.getCameraCharacteristics(it), it)
        }.filter {
            // Filter by cameras facing the requested direction
            facing == null || it.first.get(CameraCharacteristics.LENS_FACING) == facing
        }.filter {
            // Filter by logical cameras
            // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28
            it.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!!.contains(
                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)
        }.forEach {
            // All possible pairs from the list of physical cameras are valid results
            // NOTE: There could be N physical cameras as part of a logical camera grouping
            // getPhysicalCameraIds() requires API >= 28
            val physicalCameras = it.first.physicalCameraIds.toTypedArray()
            for (idx1 in 0 until physicalCameras.size) {
                for (idx2 in (idx1 + 1) until physicalCameras.size) {
                    dualCameras.add(DualCamera(
                        it.second, physicalCameras[idx1], physicalCameras[idx2]))
                }
            }
        }

        return dualCameras
    }

Java

/**
     * Helper class used to encapsulate a logical camera and two underlying
     * physical cameras
     */
    final class DualCamera {
        final String logicalId;
        final String physicalId1;
        final String physicalId2;

        DualCamera(String logicalId, String physicalId1, String physicalId2) {
            this.logicalId = logicalId;
            this.physicalId1 = physicalId1;
            this.physicalId2 = physicalId2;
        }
    }
    List findDualCameras(CameraManager manager, Integer facing) {
        List dualCameras = new ArrayList<>();

        List cameraIdList;
        try {
            cameraIdList = Arrays.asList(manager.getCameraIdList());
        } catch (CameraAccessException e) {
            e.printStackTrace();
            cameraIdList = new ArrayList<>();
        }

        // Iterate over all the available camera characteristics
        cameraIdList.stream()
                .map(id -> {
                    try {
                        CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
                        return new Pair<>(characteristics, id);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        return null;
                    }
                })
                .filter(pair -> {
                    // Filter by cameras facing the requested direction
                    return (pair != null) &&
                            (facing == null || pair.first.get(CameraCharacteristics.LENS_FACING).equals(facing));
                })
                .filter(pair -> {
                    // Filter by logical cameras
                    // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28
                    IntPredicate logicalMultiCameraPred =
                            arg -> arg == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
                    return Arrays.stream(pair.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES))
                            .anyMatch(logicalMultiCameraPred);
                })
                .forEach(pair -> {
                    // All possible pairs from the list of physical cameras are valid results
                    // NOTE: There could be N physical cameras as part of a logical camera grouping
                    // getPhysicalCameraIds() requires API >= 28
                    String[] physicalCameras = pair.first.getPhysicalCameraIds().toArray(new String[0]);
                    for (int idx1 = 0; idx1 < physicalCameras.length; idx1++) {
                        for (int idx2 = idx1 + 1; idx2 < physicalCameras.length; idx2++) {
                            dualCameras.add(
                                    new DualCamera(pair.second, physicalCameras[idx1], physicalCameras[idx2]));
                        }
                    }
                });
return dualCameras;
}

La gestione dello stato delle telecamere fisiche è controllata dalla fotocamera logica. A apri una "doppia fotocamera" aprire la fotocamera logica corrispondente al videocamere:

Kotlin

fun openDualCamera(cameraManager: CameraManager,
                       dualCamera: DualCamera,
        // AsyncTask is deprecated beginning API 30
                       executor: Executor = AsyncTask.SERIAL_EXECUTOR,
                       callback: (CameraDevice) -> Unit) {

        // openCamera() requires API >= 28
        cameraManager.openCamera(
            dualCamera.logicalId, executor, object : CameraDevice.StateCallback() {
                override fun onOpened(device: CameraDevice) = callback(device)
                // Omitting for brevity...
                override fun onError(device: CameraDevice, error: Int) = onDisconnected(device)
                override fun onDisconnected(device: CameraDevice) = device.close()
            })
    }

Java

void openDualCamera(CameraManager cameraManager,
                        DualCamera dualCamera,
                        Executor executor,
                        CameraDeviceCallback cameraDeviceCallback
    ) {

        // openCamera() requires API >= 28
        cameraManager.openCamera(dualCamera.logicalId, executor, new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice cameraDevice) {
               cameraDeviceCallback.callback(cameraDevice);
            }

            @Override
            public void onDisconnected(@NonNull CameraDevice cameraDevice) {
                cameraDevice.close();
            }

            @Override
            public void onError(@NonNull CameraDevice cameraDevice, int i) {
                onDisconnected(cameraDevice);
            }
        });
    }

Oltre a selezionare la videocamera da aprire, la procedura è la stessa dell'apertura una videocamera nelle versioni precedenti di Android. Creazione di una sessione di acquisizione utilizzando il nuovo l'API di configurazione della sessione indica al framework a cui associare determinati target ID fotocamera fisica specifici:

Kotlin

/**
 * Helper type definition that encapsulates 3 sets of output targets:
 *
 *   1. Logical camera
 *   2. First physical camera
 *   3. Second physical camera
 */
typealias DualCameraOutputs =
        Triple<MutableList?, MutableList?, MutableList?>

fun createDualCameraSession(cameraManager: CameraManager,
                            dualCamera: DualCamera,
                            targets: DualCameraOutputs,
                            // AsyncTask is deprecated beginning API 30
                            executor: Executor = AsyncTask.SERIAL_EXECUTOR,
                            callback: (CameraCaptureSession) -> Unit) {

    // Create 3 sets of output configurations: one for the logical camera, and
    // one for each of the physical cameras.
    val outputConfigsLogical = targets.first?.map { OutputConfiguration(it) }
    val outputConfigsPhysical1 = targets.second?.map {
        OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId1) } }
    val outputConfigsPhysical2 = targets.third?.map {
        OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId2) } }

    // Put all the output configurations into a single flat array
    val outputConfigsAll = arrayOf(
        outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2)
        .filterNotNull().flatMap { it }

    // Instantiate a session configuration that can be used to create a session
    val sessionConfiguration = SessionConfiguration(
        SessionConfiguration.SESSION_REGULAR,
        outputConfigsAll, executor, object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) = callback(session)
            // Omitting for brevity...
            override fun onConfigureFailed(session: CameraCaptureSession) = session.device.close()
        })

    // Open the logical camera using the previously defined function
    openDualCamera(cameraManager, dualCamera, executor = executor) {

        // Finally create the session and return via callback
        it.createCaptureSession(sessionConfiguration)
    }
}

Java

/**
 * Helper class definition that encapsulates 3 sets of output targets:
 * 

* 1. Logical camera * 2. First physical camera * 3. Second physical camera */ final class DualCameraOutputs { private final List logicalCamera; private final List firstPhysicalCamera; private final List secondPhysicalCamera; public DualCameraOutputs(List logicalCamera, List firstPhysicalCamera, List third) { this.logicalCamera = logicalCamera; this.firstPhysicalCamera = firstPhysicalCamera; this.secondPhysicalCamera = third; } public List getLogicalCamera() { return logicalCamera; } public List getFirstPhysicalCamera() { return firstPhysicalCamera; } public List getSecondPhysicalCamera() { return secondPhysicalCamera; } } interface CameraCaptureSessionCallback { void callback(CameraCaptureSession cameraCaptureSession); } void createDualCameraSession(CameraManager cameraManager, DualCamera dualCamera, DualCameraOutputs targets, Executor executor, CameraCaptureSessionCallback cameraCaptureSessionCallback) { // Create 3 sets of output configurations: one for the logical camera, and // one for each of the physical cameras. List outputConfigsLogical = targets.getLogicalCamera().stream() .map(OutputConfiguration::new) .collect(Collectors.toList()); List outputConfigsPhysical1 = targets.getFirstPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId1); return outputConfiguration; }) .collect(Collectors.toList()); List outputConfigsPhysical2 = targets.getSecondPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId2); return outputConfiguration; }) .collect(Collectors.toList()); // Put all the output configurations into a single flat array List outputConfigsAll = Stream.of( outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2 ) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); // Instantiate a session configuration that can be used to create a session SessionConfiguration sessionConfiguration = new SessionConfiguration( SessionConfiguration.SESSION_REGULAR, outputConfigsAll, executor, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSessionCallback.callback(cameraCaptureSession); } // Omitting for brevity... @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSession.getDevice().close(); } }); // Open the logical camera using the previously defined function openDualCamera(cameraManager, dualCamera, executor, (CameraDevice c) -> // Finally create the session and return via callback c.createCaptureSession(sessionConfiguration)); }

Consulta createCaptureSession per informazioni sulle combinazioni di flussi supportate. Combinazione di flussi è per più flussi su una singola videocamera logica. La compatibilità si estende a utilizzando la stessa configurazione sostituendo uno di questi stream con due da due fotocamere fisiche che fanno parte della stessa fotocamera logica.

Con sessione con la videocamera pronto, invia richieste di acquisizione. Ciascuna della richiesta di acquisizione riceve i dati dal suo indirizzo fisico videocamera logica, se sono in uso.

Esempio di caso d'uso di Zoom

È possibile utilizzare l'unione di videocamere fisiche in un unico stream in modo che che gli utenti possono passare da una fotocamera fisica all'altra per sperimentare un campo visivo diverso, consentendo di acquisire un "livello di zoom" diverso.

Figura 4. Esempio di sostituzione delle fotocamere per il caso d'uso del livello di zoom (da un annuncio di Pixel 3)

Inizia selezionando la coppia di fotocamere fisiche per consentire agli utenti di effettuare il passaggio tra di loro. Per ottenere il massimo effetto, puoi scegliere la coppia di videocamere che forniscono la lunghezza focale minima e massima disponibile.

Kotlin

fun findShortLongCameraPair(manager: CameraManager, facing: Int? = null): DualCamera? {

    return findDualCameras(manager, facing).map {
        val characteristics1 = manager.getCameraCharacteristics(it.physicalId1)
        val characteristics2 = manager.getCameraCharacteristics(it.physicalId2)

        // Query the focal lengths advertised by each physical camera
        val focalLengths1 = characteristics1.get(
            CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F)
        val focalLengths2 = characteristics2.get(
            CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F)

        // Compute the largest difference between min and max focal lengths between cameras
        val focalLengthsDiff1 = focalLengths2.maxOrNull()!! - focalLengths1.minOrNull()!!
        val focalLengthsDiff2 = focalLengths1.maxOrNull()!! - focalLengths2.minOrNull()!!

        // Return the pair of camera IDs and the difference between min and max focal lengths
        if (focalLengthsDiff1 < focalLengthsDiff2) {
            Pair(DualCamera(it.logicalId, it.physicalId1, it.physicalId2), focalLengthsDiff1)
        } else {
            Pair(DualCamera(it.logicalId, it.physicalId2, it.physicalId1), focalLengthsDiff2)
        }

        // Return only the pair with the largest difference, or null if no pairs are found
    }.maxByOrNull { it.second }?.first
}

Java

// Utility functions to find min/max value in float[]
    float findMax(float[] array) {
        float max = Float.NEGATIVE_INFINITY;
        for(float cur: array)
            max = Math.max(max, cur);
        return max;
    }
    float findMin(float[] array) {
        float min = Float.NEGATIVE_INFINITY;
        for(float cur: array)
            min = Math.min(min, cur);
        return min;
    }

DualCamera findShortLongCameraPair(CameraManager manager, Integer facing) {
        return findDualCameras(manager, facing).stream()
                .map(c -> {
                    CameraCharacteristics characteristics1;
                    CameraCharacteristics characteristics2;
                    try {
                        characteristics1 = manager.getCameraCharacteristics(c.physicalId1);
                        characteristics2 = manager.getCameraCharacteristics(c.physicalId2);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        return null;
                    }

                    // Query the focal lengths advertised by each physical camera
                    float[] focalLengths1 = characteristics1.get(
                            CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
                    float[] focalLengths2 = characteristics2.get(
                            CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);

                    // Compute the largest difference between min and max focal lengths between cameras
                    Float focalLengthsDiff1 = findMax(focalLengths2) - findMin(focalLengths1);
                    Float focalLengthsDiff2 = findMax(focalLengths1) - findMin(focalLengths2);

                    // Return the pair of camera IDs and the difference between min and max focal lengths
                    if (focalLengthsDiff1 < focalLengthsDiff2) {
                        return new Pair<>(new DualCamera(c.logicalId, c.physicalId1, c.physicalId2), focalLengthsDiff1);
                    } else {
                        return new Pair<>(new DualCamera(c.logicalId, c.physicalId2, c.physicalId1), focalLengthsDiff2);
                    }

                }) // Return only the pair with the largest difference, or null if no pairs are found
                .max(Comparator.comparing(pair -> pair.second)).get().first;
    }

Un'architettura sensata per farlo sarebbe avere due SurfaceViews: uno per ogni stream. Questi SurfaceViews vengono scambiati in base all'interazione dell'utente, in modo che solo uno sia visibili in qualsiasi momento.

Il seguente codice mostra come aprire la fotocamera logica e configurare la fotocamera crea una sessione di videocamera e avvia due stream di anteprima:

Kotlin

val cameraManager: CameraManager = ...

// Get the two output targets from the activity / fragment
val surface1 = ...  // from SurfaceView
val surface2 = ...  // from SurfaceView

val dualCamera = findShortLongCameraPair(manager)!!
val outputTargets = DualCameraOutputs(
    null, mutableListOf(surface1), mutableListOf(surface2))

// Here you open the logical camera, configure the outputs and create a session
createDualCameraSession(manager, dualCamera, targets = outputTargets) { session ->

  // Create a single request which has one target for each physical camera
  // NOTE: Each target receive frames from only its associated physical camera
  val requestTemplate = CameraDevice.TEMPLATE_PREVIEW
  val captureRequest = session.device.createCaptureRequest(requestTemplate).apply {
    arrayOf(surface1, surface2).forEach { addTarget(it) }
  }.build()

  // Set the sticky request for the session and you are done
  session.setRepeatingRequest(captureRequest, null, null)
}

Java

CameraManager manager = ...;

        // Get the two output targets from the activity / fragment
        Surface surface1 = ...;  // from SurfaceView
        Surface surface2 = ...;  // from SurfaceView

        DualCamera dualCamera = findShortLongCameraPair(manager, null);
                DualCameraOutputs outputTargets = new DualCameraOutputs(
                null, Collections.singletonList(surface1), Collections.singletonList(surface2));

        // Here you open the logical camera, configure the outputs and create a session
        createDualCameraSession(manager, dualCamera, outputTargets, null, (session) -> {
            // Create a single request which has one target for each physical camera
            // NOTE: Each target receive frames from only its associated physical camera
            CaptureRequest.Builder captureRequestBuilder;
            try {
                captureRequestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                Arrays.asList(surface1, surface2).forEach(captureRequestBuilder::addTarget);

                // Set the sticky request for the session and you are done
                session.setRepeatingRequest(captureRequestBuilder.build(), null, null);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        });

Tutto ciò che devi fare è fornire all'utente un'interfaccia utente per passare da uno all'altro come un pulsante o il doppio tocco di SurfaceView. Potresti persino eseguire un'analisi della scena e passare da un flusso all'altro automaticamente.

Distorsione dell'obiettivo

Tutti gli obiettivi producono una certa quantità di distorsione. In Android, puoi eseguire query distorsione creata da obiettivi utilizzando CameraCharacteristics.LENS_DISTORTION, che sostituisce l'ormai deprecato CameraCharacteristics.LENS_RADIAL_DISTORTION. Per le fotocamere logiche, la distorsione è minima e l'applicazione può utilizzare i fotogrammi più o meno come provengono dalla fotocamera. Per le fotocamere fisiche ci sono configurazioni di obiettivi potenzialmente molto diverse, soprattutto per i modelli grandangolare lenti.

Alcuni dispositivi possono implementare la correzione automatica delle distorsioni tramite CaptureRequest.DISTORTION_CORRECTION_MODE Per impostazione predefinita, la correzione della distorsione è attiva per la maggior parte dei dispositivi.

Kotlin

val cameraSession: CameraCaptureSession = ...

        // Use still capture template to build the capture request
        val captureRequest = cameraSession.device.createCaptureRequest(
            CameraDevice.TEMPLATE_STILL_CAPTURE
        )

        // Determine if this device supports distortion correction
        val characteristics: CameraCharacteristics = ...
        val supportsDistortionCorrection = characteristics.get(
            CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES
        )?.contains(
            CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY
        ) ?: false

        if (supportsDistortionCorrection) {
            captureRequest.set(
                CaptureRequest.DISTORTION_CORRECTION_MODE,
                CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY
            )
        }

        // Add output target, set other capture request parameters...

        // Dispatch the capture request
        cameraSession.capture(captureRequest.build(), ...)

Java

CameraCaptureSession cameraSession = ...;

        // Use still capture template to build the capture request
        CaptureRequest.Builder captureRequestBuilder = null;
        try {
            captureRequestBuilder = cameraSession.getDevice().createCaptureRequest(
                    CameraDevice.TEMPLATE_STILL_CAPTURE
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        // Determine if this device supports distortion correction
        CameraCharacteristics characteristics = ...;
        boolean supportsDistortionCorrection = Arrays.stream(
                        characteristics.get(
                                CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES
                        ))
                .anyMatch(i -> i == CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY);
        if (supportsDistortionCorrection) {
            captureRequestBuilder.set(
                    CaptureRequest.DISTORTION_CORRECTION_MODE,
                    CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY
            );
        }

        // Add output target, set other capture request parameters...

        // Dispatch the capture request
        cameraSession.capture(captureRequestBuilder.build(), ...);

L'impostazione di una richiesta di acquisizione in questa modalità può influire sulla frequenza fotogrammi prodotto dalla fotocamera. Puoi scegliere di impostare la correzione delle distorsioni solo su acquisizioni di immagini statiche.