Hinweis:Diese Seite bezieht sich auf das Camera2-Paket. Sofern für Ihre App keine spezifischen Low-Level-Funktionen von Camera2 erforderlich sind, empfehlen wir die Verwendung von CameraX. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.
Die Verwendung mehrerer Kameras wurde mit Android 9 (API-Level 28) eingeführt. Seit der Veröffentlichung Geräte auf den Markt gebracht werden, die die API unterstützen. Viele Anwendungsfälle mit mehreren Kameras eng mit einer bestimmten Hardwarekonfiguration gekoppelt. Mit anderen Worten: Alle Anwendungsfälle sind mit jedem Gerät kompatibel, sodass mehrere Kameras eine gute Wahl für die Wiedergabefunktion ist. Auslieferung:
Hier einige typische Anwendungsfälle:
- Zoom: Schaltet je nach Zuschnittbereich oder gewünschtem Fokus zwischen den Kameras um. Länge.
- Tiefe: Erstellung einer Tiefenkarte durch mehrere Kameras
- Bokeh: Die Verwendung abgeleiteter Tiefeninformationen zur Simulation einer schmalen Spiegelreflexkamera (Spiegelreflexkamera) Fokusbereich
Der Unterschied zwischen logischen und physischen Kameras
Um das API für mehrere Kameras zu verstehen, müssen Sie den Unterschied zwischen logische und physische Kameras. Betrachten wir ein Gerät mit drei rückseitigen Kameras. In diesem Beispiel ist jede der drei Rückkameras als physische Kamera betrachtet. Eine logische Kamera ist eine Gruppierung aus zwei oder mehr dieser physischen Kameras. Die Ausgabe der logischen Kamera kann ein Stream von einer der zugrunde liegenden physischen Kameras sein. oder ein zusammengeführter Strom von mehr als einer zugrunde liegenden physischen Kamera gleichzeitig. In beiden Fällen wird der Stream von der Kamera-Hardware verarbeitet. Abstraktionsschicht (HAL).
Viele Smartphone-Hersteller entwickeln eigene Kamera-Apps, auf ihren Geräten vorinstalliert sind. Um alle Funktionen der Hardware zu nutzen, private oder versteckte APIs nutzen oder gesondert behandelt werden, der Treiberimplementierung, auf die andere Anwendungen keinen Zugriff haben. Einige Geräte implementieren das Konzept logischer Kameras, indem sie den verschiedenen physischen Kameras, sondern nur von bestimmten privilegierten Geräten Anwendungen. Häufig ist nur eine der physischen Kameras mit dem Framework. Die Situation für Drittanbieter-Entwickler vor Android 9 wie in der folgenden Abbildung dargestellt:
<ph type="x-smartling-placeholder">![](https://developer.android.com/static/images/training/camera/camera2/multi-camera-1.png?authuser=9&hl=de)
Ab Android 9 sind private APIs in Android-Apps nicht mehr zulässig. Durch die Unterstützung mehrerer Kameras im Framework ist Android Smartphone-Herstellern unbedingt eine logische Kamera für alle physischen Kameras, die in die gleiche Richtung zeigen. Im Folgenden finden Sie Drittanbieter-Entwickler auf Geräten mit Android 9 und höher:
<ph type="x-smartling-placeholder">![](https://developer.android.com/static/images/training/camera/camera2/multi-camera-2.png?authuser=9&hl=de)
Was die logische Kamera bietet, hängt vollständig von der OEM-Implementierung ab. der Kamera-HAL. Beispielsweise implementiert ein Gerät wie Pixel 3 seine logische Kamera so konfigurieren, dass eine ihrer physischen Kameras anhand des die angeforderte Brennweite und Zuschneidebereich.
API für mehrere Kameras
Mit der neuen API werden die folgenden neuen Konstanten, Klassen und Methoden hinzugefügt:
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
CameraCharacteristics.getPhysicalCameraIds()
CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()
CameraDevice.createCaptureSession(SessionConfiguration config)
CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
OutputConfiguration
undSessionConfiguration
Aufgrund von Änderungen am CDD (Android Compatibility Definition Document) Die Multi-Kamera-API bringt auch bestimmte Erwartungen von Entwicklern mit sich. Geräte mit Dual-Kameras gab es schon vor Android 9. Es wurden aber mehr als eine Kamera geöffnet. gleichzeitig Trial-and-Error-Methode. Unter Android 9 und höher: mehrere Kameras gibt eine Reihe von Regeln an, mit denen angegeben wird, wann ein Paar physischer Kameras, die zur selben logischen Kamera gehören.
In den meisten Fällen sind Geräte mit Android 9 und höher Kameras (mit Ausnahme weniger üblicher Sensortypen wie Infrarot) und eine benutzerfreundlichere logische Kamera. Für alle Arten von Streams, die funktioniert, kann ein Stream einer logischen Kamera durch einen zwei Streams von den zugrunde liegenden physischen Kameras.
Mehrere Streams gleichzeitig
Mehrere Kamerastreams gleichzeitig verwenden
behandelt die Regeln für die gleichzeitige Verwendung mehrerer Streams in einer einzelnen Kamera.
Es gibt jedoch eine weitere wichtige Neuerung: Für mehrere Kameras gelten dieselben Regeln.
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
wird erläutert, wie ein logischer YUV_420_888 oder ein Rohstream durch zwei
physischen Streams. Das heißt, jeder Stream des Typs YUV oder RAW kann durch
zwei Streams identischem Typ und identischer Größe. Sie können mit einem Kamerastream von
folgende garantierte Konfiguration für Geräte mit einer Kamera:
- Stream 1: YUV-Typ,
MAXIMUM
-Größe von logischer Kameraid = 0
Dann können Sie mit einem Gerät, das mehrere Kameras unterstützt, eine Sitzung erstellen. Dabei wird der logische YUV-Stream durch zwei physische Streams ersetzt:
- Stream 1: YUV-Typ,
MAXIMUM
-Größe der physischen Kameraid = 1
- Stream 2: YUV-Typ,
MAXIMUM
-Größe der physischen Kameraid = 2
Du kannst einen YUV- oder RAW-Stream nur dann durch zwei gleichwertige Streams ersetzen, wenn
sind diese beiden Kameras Teil einer logischen Kameragruppe, die unter
CameraCharacteristics.getPhysicalCameraIds()
Die vom Framework gebotenen Garantien sind nur das absolute Minimum, das für Bilder von mehr als einer physischen Kamera gleichzeitig erhalten. Zusätzliche Streams werden auf den meisten Geräten unterstützt, manchmal können sogar mehrere physische Kamerageräte unabhängig voneinander ab. Da dies keine feste Garantie Framework. Dazu müssen Sie gerätespezifische Tests und Feinabstimmungen mit durch das Ausprobieren von Versuch und Irrtum.
Sitzung mit mehreren physischen Kameras erstellen
Wenn Sie physische Kameras an einem Gerät verwenden, das mehrere Kameras unterstützt, öffnen Sie ein einzelnes
CameraDevice
(logische Kamera) und interagieren mit ihr innerhalb eines
Sitzung. Einzelne Sitzung mithilfe der API erstellen
CameraDevice.createCaptureSession(SessionConfiguration config)
, was
in API-Ebene 28 hinzugefügt. Die Sitzungskonfiguration enthält eine Reihe von
Konfigurationen, die jeweils eine Reihe von Ausgabezielen und optional
die gewünschte ID der physischen Kamera.
![](https://developer.android.com/static/images/training/camera/camera2/multi-camera-3.png?authuser=9&hl=de)
Erfassungsanfragen ist ein Ausgabeziel zugeordnet. Das Framework bestimmt, an welche physische (oder logische) Kamera die Anfragen gesendet werden. welches Ausgabeziel angehängt wird. Wenn das Ausgabeziel einem der Ausgabeziele, die als Ausgabekonfiguration zusammen mit einem physischen Kamera-ID enthält, empfängt und verarbeitet die physische Kamera die Anfrage.
Verwendung von zwei physischen Kameras
Eine weitere Ergänzung der Kamera-APIs für mehrere Kameras ist die Möglichkeit, logische Kameras und ermitteln Sie die physischen Kameras dahinter. Sie können eine um potenzielle Kamerapaare zu identifizieren, die Sie nutzen können um einen der logischen Kamerastreams zu ersetzen:
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; } } ListfindDualCameras(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; }
Die Statusverarbeitung der physischen Kameras wird von der logischen Kamera gesteuert. Bis eine „Dual-Kamera“ öffnen, die logische Kamera öffnen, die der physischen Kamera entspricht Kameras:
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); } }); }
Der Vorgang zum Öffnen der Kamera ist der gleiche wie beim Öffnen. eine Kamera in früheren Android-Versionen. Erstellen einer Aufnahmesitzung mit dem neuen Sitzungskonfigurations-API weist das Framework an, bestimmte Ziele mit bestimmte IDs physischer Kameras:
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)); }
Weitere Informationen finden Sie unter
createCaptureSession
finden Sie Informationen dazu, welche Kombination von Streams unterstützt wird. Streams kombinieren
für mehrere Streams über eine einzige logische Kamera. Die Kompatibilität erstreckt sich
mit derselben Konfiguration, wobei einer dieser Streams durch zwei Streams ersetzt wird.
zwei physische Kameras, die Teil derselben logischen Kamera sind.
Mit der Kamerasitzung fertig sind, senden Sie die gewünschten Erfassungsanfragen. Jedes der Erfassungsanfrage ihre Daten vom zugehörigen physischen Kamera verwenden, falls welche verwendet werden, oder auf die logische Kamera zurückgreifen.
Beispiel für Zoom – Anwendungsfall
Durch die Zusammenführung physischer Kameras zu einem einzigen Stream um zwischen verschiedenen physischen Kameras zu wechseln, Sichtfeld und damit eine andere „Zoomstufe“.
<ph type="x-smartling-placeholder">![](https://developer.android.com/static/images/training/camera/camera2/multi-camera-4.gif?authuser=9&hl=de)
Wählen Sie zuerst zwei physische Kameras aus, damit Nutzer die Kamera wechseln können dazwischen liegt. Die besten Ergebnisse erzielen Sie, wenn Sie zwei Kameras auswählen, die der verfügbaren minimalen und maximalen Brennweite.
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; }
Eine sinnvolle Architektur dafür wären zwei
SurfaceViews
: eine pro Stream
Diese SurfaceViews
werden auf Grundlage der Nutzerinteraktion vertauscht, sodass nur eine davon
zu einem bestimmten Zeitpunkt sichtbar sind.
Der folgende Code zeigt, wie die logische Kamera geöffnet und die Kamera konfiguriert wird. Ausgabe, erstellen Sie eine Kamerasitzung und starten Sie zwei Vorschaustreams:
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(); } });
Nun müssen die Nutzenden nur noch eine Benutzeroberfläche bereitstellen, über die sie zwischen den beiden wechseln können.
z. B. eine Schaltfläche oder das Doppeltippen auf SurfaceView
. Sie könnten sogar
Szenenanalysen durchzuführen und zwischen den beiden Streams zu wechseln
automatisch.
Objektivverzeichnung
Alle Objektive erzeugen eine gewisse Verzerrung. In Android können Sie die
durch Linsen erzeugte Verzerrungen,
CameraCharacteristics.LENS_DISTORTION
,
die das mittlerweile eingestellte
CameraCharacteristics.LENS_RADIAL_DISTORTION
Bei logischen Kameras ist die Verzerrung minimal und Ihre Anwendung kann
je nachdem, wie die Bilder von der Kamera kommen. Bei physischen Kameras:
sind möglicherweise sehr unterschiedliche Objektivkonfigurationen, insbesondere das Weitwinkelobjektiv,
Objektive.
Einige Geräte implementieren die automatische Verzeichnungskorrektur
CaptureRequest.DISTORTION_CORRECTION_MODE
Die Verzeichnungskorrektur ist bei den meisten Geräten standardmäßig aktiviert.
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(), ...);
Das Festlegen einer Aufnahmeanfrage in diesem Modus kann sich auf die Framerate auswirken, der von der Kamera erzeugt wird. Sie können die Verzeichnungskorrektur nur auf Standbilder erfasst.