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.
Un singolo dispositivo Android può avere più fotocamere. Ogni videocamera è un
CameraDevice
,
e un CameraDevice
può produrre più di uno stream contemporaneamente.
Uno dei motivi per farlo è fare in modo che uno stream, i fotogrammi sequenziali della videocamera
da un CameraDevice
, è ottimizzato per un'attività specifica, come la visualizzazione
un mirino, mentre altri potrebbero essere usati per scattare una foto o per realizzare un video
I flussi agiscono come pipeline parallele che elaborano i frame non elaborati.
che esce dalla fotocamera,un fotogramma alla volta:
L'elaborazione parallela suggerisce che potrebbero esserci limiti di prestazioni a seconda la potenza di elaborazione disponibile dalla CPU, dalla GPU o dall'altro processore. Se la pipeline non riesce a stare al passo con i frame in entrata e inizia a interromperli.
Ogni pipeline ha il proprio formato di output. I dati non elaborati in entrata
automaticamente trasformati in
formato di output per logica implicita
associati a ogni pipeline. Il valore CameraDevice
utilizzato in questa pagina
esempi di codice non sono specifici, quindi devi prima enumerare
tutte le videocamere disponibili prima di procedere.
Puoi utilizzare CameraDevice
per creare un
CameraCaptureSession
,
che è specifico per quel determinato CameraDevice
. Un CameraDevice
deve ricevere un
per ogni frame non elaborato utilizzando CameraCaptureSession
. La
specifica gli attributi della fotocamera, come messa a fuoco automatica, apertura, effetti
ed esposizione. A causa di vincoli hardware, è possibile configurare una sola configurazione
attiva nel sensore della fotocamera, che è chiamato
active.
Tuttavia, i casi d'uso di Stream migliorano ed estendono le modalità di utilizzo precedenti di CameraDevice
per le sessioni di acquisizione dello streaming, che ti consentono di ottimizzare lo streaming della videocamera
caso d'uso specifico. Ad esempio, può migliorare la durata della batteria durante l'ottimizzazione
videochiamate.
Un CameraCaptureSession
descrive tutte le possibili pipeline associate all'oggetto
CameraDevice
. Quando viene creata una sessione, non puoi aggiungere o rimuovere pipeline.
CameraCaptureSession
mantiene una coda di
CaptureRequest
,
che diventano la configurazione attiva.
Un CaptureRequest
aggiunge una configurazione alla coda e ne seleziona una più di
una o tutte le pipeline disponibili per ricevere un frame
CameraDevice
. Puoi inviare molte richieste di acquisizione durante il ciclo di vita di un'acquisizione
durante la sessione. Ogni richiesta può modificare la configurazione attiva e l'insieme di output
e le pipeline che ricevono l'immagine non elaborata.
Utilizzare i casi d'uso di Stream per migliorare le prestazioni
I casi d'uso di streaming sono un modo per migliorare le prestazioni dell'acquisizione di Fotocamera 2 sessioni. Offrono al dispositivo hardware più informazioni per regolare i parametri, che offre una migliore esperienza con la videocamera per la tua attività specifica.
Questo
Consente alla videocamera di ottimizzare le pipeline hardware e software della videocamera
in base agli scenari utente per ogni stream. Per ulteriori informazioni sull'utilizzo dello stream
Richieste di assistenza, vedi setStreamUseCase
.
I casi d'uso di streaming consentono di specificare come viene utilizzato lo stream di una videocamera
maggiori dettagli, oltre a impostare un modello
CameraDevice.createCaptureRequest()
. Ciò consente all'hardware della videocamera di ottimizzare
come la regolazione, la modalità o le impostazioni del sensore della fotocamera, in base a
di latenza e qualità adatti a casi d'uso specifici.
I casi d'uso degli stream includono:
DEFAULT
: copre tutti i comportamenti dell'applicazione esistenti. Equivale a non l'impostazione di qualsiasi caso d'uso di stream.PREVIEW
: consigliata per il mirino o l'analisi delle immagini in-app.STILL_CAPTURE
: l'obiettivo è ottimizzato per acquisizioni in alta qualità e ad alta risoluzione, ma non si prevede che mantengano frequenze fotogrammi simili a quelle delle anteprime.VIDEO_RECORD
: Ottimizzata per l'acquisizione di video di alta qualità, anche di alta qualità stabilizzazione dell'immagine, se supportata dal dispositivo e abilitata dall'applicazione. Questa opzione potrebbe produrre frame di output con un ritardo sostanziale rispetto al tempo reale, per consentire una stabilizzazione o altre elaborazioni di alta qualità.VIDEO_CALL
: consigliata per fotocamere di lunga durata in cui il consumo di energia è elevato problema.PREVIEW_VIDEO_STILL
: consigliata per le app di social media o l'uso di singoli stream d'uso diversi. È uno stream polivalente.VENDOR_START
: per casi d'uso definiti dagli OEM.
Crea un Camera CaptureSession
Per creare una sessione della videocamera, forniscile uno o più buffer di output in cui l'app può scrivere i frame di output. Ogni buffer rappresenta una pipeline. Devi prima di iniziare a usare la fotocamera, in modo che il framework possa configurare alle pipeline interne del dispositivo e allocano buffer di memoria per l'invio di frame alle destinazioni di output necessarie.
Il seguente snippet di codice mostra come preparare una sessione della videocamera con
buffer di output, uno appartenente a un
SurfaceView
e un altro a un
ImageReader
. Aggiunta del caso d'uso dello stream PREVIEW
a previewSurface
e
Utilizzo dello streaming STILL_CAPTURE
L'impostazione case to imReaderSurface
consente all'hardware del dispositivo di ottimizzare questi stream anche
ulteriormente.
Kotlin
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewById<SurfaceView>(...) val imageReader = ImageReader.newInstance(...) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: List<Surface>){ val configs = mutableListOf<OutputConfiguration>() val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } ... device.createCaptureSession(session) }
Java
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis // 3. RenderScript.Allocation, if you want to do parallel processing // 4. OpenGL Texture or TextureView, although discouraged for maintainability reasons Surface surfaceView = findViewById<SurfaceView>(...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); List<Surface> targets = Arrays.asList(previewSurface, imageSurface); // Create a capture session using the predefined targets; this also involves defining the // session state callback to be notified of when the session is ready private void configureSession(CameraDevice device, List<Surface> targets){ ArrayList<OutputConfiguration> configs= new ArrayList() String streamUseCase= CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL for(Surface s : targets){ OutputConfiguration config = new OutputConfiguration(s) config.setStreamUseCase(String.toLong(streamUseCase)) configs.add(config) } device.createCaptureSession(session) }
A questo punto, non hai definito la configurazione attiva della videocamera. Una volta configurata la sessione, puoi creare e inviare acquisizioni richiede molto lavoro.
La trasformazione applicata agli input così come vengono scritti nel buffer è
determinato dal tipo di ciascun target, che deve essere un
Surface
Il framework Android sa come
convertire un'immagine non elaborata dalla configurazione attiva in un formato appropriato per
per ogni target. La conversione è controllata dal formato e dalle dimensioni dei pixel
particolare Surface
.
Il framework cerca di dare il meglio, ma alcune Surface
di configurazione potrebbero non funzionare, causando problemi come la sessione
non viene creato, viene generato un errore di runtime quando invii una richiesta
un peggioramento delle prestazioni. Il framework fornisce garanzie per specifici
combinazioni di parametri di dispositivo, superficie e richiesta. La documentazione per
createCaptureSession()
fornisce ulteriori informazioni.
Richieste di acquisizione singole
La configurazione utilizzata per ogni frame è codificata in un CaptureRequest
, che viene
inviati alla fotocamera. Per creare una richiesta di acquisizione, puoi utilizzare una delle seguenti opzioni
predefinita
modelli,
oppure TEMPLATE_MANUAL
per avere il controllo completo. Quando scegli un
devi fornire uno o più buffer di output da utilizzare
la richiesta. Puoi usare solo i buffer già definiti nell'acquisizione
che intendi utilizzare.
Le richieste di acquisizione utilizzano un
pattern del builder
e offrono agli sviluppatori l'opportunità di impostare molte diverse
tra cui
esposizione automatica,
messa a fuoco automatica,
e
Apertura dell'obiettivo.
Prima di impostare un campo, assicurati che sia disponibile l'opzione specifica per il parametro
dispositivo chiamando
CameraCharacteristics.getAvailableCaptureRequestKeys()
e che il valore desiderato sia supportato controllando la fotocamera appropriata
come l'esposizione automatica disponibile
diverse.
Per creare una richiesta di acquisizione per un SurfaceView
utilizzando il modello
progettata per un'anteprima senza alcuna modifica, utilizza
CameraDevice.TEMPLATE_PREVIEW
:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequest.addTarget(previewSurface);
Dopo aver definito una richiesta di acquisizione, ora puoi inviare alla sessione della videocamera:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed // capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null);
Quando un frame di output viene inserito nel buffer specifico, viene
da richiamare
viene attivato. In molti casi, i callback aggiuntivi, come
ImageReader.OnImageAvailableListener
,
viene attivato quando il frame che contiene viene elaborato. È alle ore
questo punto che è possibile recuperare i dati di immagine dal buffer specificato.
Richieste di acquisizione ripetute
Le richieste con una singola videocamera sono semplici da eseguire, ma per mostrare un'immagine un'anteprima o un video, non sono molto utili. In questo caso, devi ricevere un flusso continuo di frame, non solo uno. Il seguente snippet di codice mostra come aggiungere un richiesta ripetuta alla sessione:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null);
Una richiesta di acquisizione ripetuta consente alla fotocamera di acquisire continuamente
immagini utilizzando le impostazioni nel CaptureRequest
fornito. L'API Camera2
consente inoltre agli utenti di acquisire video dalla fotocamera inviando
si ripete CaptureRequests
come visto in questa
Esempio di Camera2
su GitHub. Può anche eseguire il rendering di video in slow motion registrando un'immagine
video ad alta velocità (slow motion) con serie di foto a raffica CaptureRequests
come mostrato nell'app di esempio video in slow motion di Camera2
su GitHub.
Interfoliazione delle richieste di acquisizione
Per inviare una seconda richiesta di acquisizione mentre la richiesta di acquisizione ripetuta è attiva: ad esempio per visualizzare un mirino e consentire agli utenti di scattare una foto, non è necessario interrompere la richiesta ricorrente in corso. Devi inviare un'acquisizione non ripetuta mentre continua a essere eseguita.
Qualsiasi buffer di output utilizzato deve essere configurato come parte della sessione della videocamera quando la sessione viene creata per la prima volta. Le richieste ripetute hanno una priorità inferiore rispetto a richieste a frame singolo o di burst, che consentono il funzionamento del seguente esempio:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it CaptureRequest.Builder repeatingRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); repeatingRequest.addTarget(previewSurface); session.setRepeatingRequest(repeatingRequest.build(), null, null); // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily CaptureRequest.Builder singleRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); singleRequest.addTarget(imReaderSurface); session.capture(singleRequest.build(), null, null);
Questo approccio presenta però uno svantaggio: non sai esattamente quando quando si verifica la singola richiesta. Nella figura seguente, se A è la si ripete richiesta di acquisizione e B è la richiesta di acquisizione di frame singolo, questa è la modalità di questa sessione elabora la coda di richieste:
Non esiste alcuna garanzia per la latenza tra l'ultima richiesta ripetuta da A prima dell'attivazione della richiesta B e la volta successiva che verrà utilizzato A di nuovo, quindi è possibile che alcuni frame vengano saltati. Ci sono alcuni aspetti per mitigare il problema:
Aggiungi i target di output dalla richiesta A alla richiesta B. In questo modo, quando Il frame di B è pronto, viene copiato nelle destinazioni di output di A. Ad esempio, questo è essenziale quando si creano istantanee video per mantenere una frequenza fotogrammi stabile. Nel codice precedente, aggiungi
singleRequest.addTarget(previewSurface)
prima di creare la richiesta.Usa una combinazione di modelli adatti a questo particolare scenario, ad esempio zero-shutter-lag.