1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.hardware.camera2.utils.HashCodeHelpers;
23 
24 import java.util.concurrent.Executor;
25 
26 /**
27  * A camera capture session that enables access to device-specific camera extensions, which
28  * often use multi-frame bursts and sophisticated post-process algorithms for image capture.
29  *
30  * <p>The capture session will be returned after a successful call to
31  * {@link CameraDevice#createExtensionSession} as part of the argument
32  * in the registered state callback {@link StateCallback#onConfigured}
33  * method. </p>
34  *
35  * <p>Note that CameraExtensionSession is currently limited to a maximum of two output
36  * surfaces for continuous repeating and multi-frame processing respectively. Some
37  * features such as capture settings will not be supported as the device-specific
38  * Extension is allowed to override all capture parameters.</p>
39  *
40  * <p>Information about support for specific device-specific extensions can be queried
41  * from {@link CameraExtensionCharacteristics}. </p>
42  */
43 public abstract class CameraExtensionSession implements AutoCloseable {
44      /** @hide */
CameraExtensionSession()45     public CameraExtensionSession () {}
46 
47     /**
48      * A callback object for tracking the progress of a
49      * {@link CaptureRequest} submitted to the camera device.
50      *
51      * <p>This callback is invoked when a request triggers a capture to start,
52      * and when the device-specific Extension post processing begins. In case of an
53      * error capturing an image, the error method is triggered instead of
54      * the completion method.</p>
55      *
56      * @see #capture
57      * @see #setRepeatingRequest
58      */
59     public static abstract class ExtensionCaptureCallback {
60 
61         /**
62          * This method is called when the camera device has started
63          * capturing the initial input image of the device-specific extension
64          * post-process request.
65          *
66          * <p>This callback is invoked right as the capture of a frame begins,
67          * so it is the most appropriate time for playing a shutter sound,
68          * or triggering UI indicators of capture.</p>
69          *
70          * <p>The request that is being used for this capture is provided,
71          * along with the actual timestamp for the start of exposure.</p>
72          *
73          * <p>The default implementation of this method does nothing.</p>
74          *
75          * @param session   the session received during
76          *                  {@link StateCallback#onConfigured(CameraExtensionSession)}
77          * @param request   the request for the capture that just begun
78          * @param timestamp the timestamp at start of capture for repeating
79          *                  request or the timestamp at start of capture of the
80          *                  first frame in a multi-frame capture.
81          */
onCaptureStarted(@onNull CameraExtensionSession session, @NonNull CaptureRequest request, long timestamp)82         public void onCaptureStarted(@NonNull CameraExtensionSession session,
83                 @NonNull CaptureRequest request, long timestamp) {
84             // default empty implementation
85         }
86 
87         /**
88          * This method is called when an image (or images in case of multi-frame
89          * capture) is captured and device-specific extension
90          * processing is triggered.
91          *
92          * <p>Each request will generate at most {@code 1}
93          * {@link #onCaptureProcessStarted}.</p>
94          *
95          * <p>The default implementation of this method does nothing.</p>
96          *
97          * @param session the session received during
98          *                {@link StateCallback#onConfigured(CameraExtensionSession)}
99          * @param request The request that was given to the CameraExtensionSession
100          *
101          * @see #capture
102          * @see #setRepeatingRequest
103          */
onCaptureProcessStarted(@onNull CameraExtensionSession session, @NonNull CaptureRequest request)104         public void onCaptureProcessStarted(@NonNull CameraExtensionSession session,
105                 @NonNull CaptureRequest request) {
106             // default empty implementation
107         }
108 
109         /**
110          * This method is called instead of
111          * {@link #onCaptureProcessStarted} when the camera device failed
112          * to produce the required input for the device-specific extension. The
113          * cause could be a failed camera capture request, a failed
114          * capture result or dropped camera frame.
115          *
116          * <p>Other requests are unaffected, and some or all image buffers
117          * from the capture may have been pushed to their respective output
118          * streams.</p>
119          *
120          * <p>The default implementation of this method does nothing.</p>
121          *
122          * @param session the session received during
123          *                {@link StateCallback#onConfigured(CameraExtensionSession)}
124          * @param request The request that was given to the CameraDevice
125          *
126          * @see #capture
127          * @see #setRepeatingRequest
128          */
onCaptureFailed(@onNull CameraExtensionSession session, @NonNull CaptureRequest request)129         public void onCaptureFailed(@NonNull CameraExtensionSession session,
130                 @NonNull CaptureRequest request) {
131             // default empty implementation
132         }
133 
134         /**
135          * This method is called independently of the others in
136          * ExtensionCaptureCallback, when a capture sequence finishes.
137          *
138          * <p>In total, there will be at least one
139          * {@link #onCaptureProcessStarted}/{@link #onCaptureFailed}
140          * invocation before this callback is triggered. If the capture
141          * sequence is aborted before any requests have begun processing,
142          * {@link #onCaptureSequenceAborted} is invoked instead.</p>
143          *
144          * <p>The default implementation does nothing.</p>
145          *
146          * @param session    the session received during
147          *                   {@link StateCallback#onConfigured(CameraExtensionSession)}
148          * @param sequenceId A sequence ID returned by the {@link #capture}
149          *                   family of functions.
150          * @see #onCaptureSequenceAborted
151          */
onCaptureSequenceCompleted(@onNull CameraExtensionSession session, int sequenceId)152         public void onCaptureSequenceCompleted(@NonNull CameraExtensionSession session,
153                 int sequenceId) {
154             // default empty implementation
155         }
156 
157         /**
158          * This method is called when a capture sequence aborts.
159          *
160          * <p>Due to the asynchronous nature of the camera device, not all
161          * submitted captures are immediately processed. It is possible to
162          * clear out the pending requests by a variety of operations such
163          * as {@link CameraExtensionSession#stopRepeating}. When such an event
164          * happens, {@link #onCaptureProcessStarted} will not be called.</p>
165          *
166          * <p>The default implementation does nothing.</p>
167          *
168          * @param session    the session received during
169          *                   {@link StateCallback#onConfigured(CameraExtensionSession)}
170          * @param sequenceId A sequence ID returned by the {@link #capture}
171          *                   family of functions.
172          * @see #onCaptureProcessStarted
173          */
onCaptureSequenceAborted(@onNull CameraExtensionSession session, int sequenceId)174         public void onCaptureSequenceAborted(@NonNull CameraExtensionSession session,
175                 int sequenceId) {
176             // default empty implementation
177         }
178 
179         /**
180          * This method is called when an image capture has fully completed and all the
181          * result metadata is available.
182          *
183          * <p>This callback will only be called in case
184          * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys} returns a valid
185          * non-empty list.</p>
186          *
187          * <p>The default implementation of this method does nothing.</p>
188          *
189          * @param session The session received during
190          *                {@link StateCallback#onConfigured(CameraExtensionSession)}
191          * @param request The request that was given to the CameraDevice
192          * @param result The total output metadata from the capture, which only includes the
193          * capture result keys advertised as supported in
194          * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys}.
195          *
196          * @see #capture
197          * @see #setRepeatingRequest
198          * @see CameraExtensionCharacteristics#getAvailableCaptureResultKeys
199          */
onCaptureResultAvailable(@onNull CameraExtensionSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result)200         public void onCaptureResultAvailable(@NonNull CameraExtensionSession session,
201                 @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
202             // default empty implementation
203         }
204 
205         /**
206          * This method is called when image capture processing is ongoing between
207          * {@link #onCaptureProcessStarted} and the processed still capture frame returning
208          * to the client surface.
209          *
210          * <p>The value included in the arguments provides clients with an estimate
211          * of the post-processing progress which could take significantly more time
212          * relative to the rest of the {@link #capture} sequence.</p>
213          *
214          * <p>The callback will be triggered only by extensions that return {@code true}
215          * from calls
216          * {@link CameraExtensionCharacteristics#isCaptureProcessProgressAvailable}.</p>
217          *
218          * <p>If support for this callback is present, then clients will be notified at least once
219          * with progress value 100.</p>
220          *
221          * <p>The callback will be triggered only for still capture requests {@link #capture} and
222          * is not supported for repeating requests {@link #setRepeatingRequest}.</p>
223          *
224          * <p>The default implementation of this method does nothing.</p>
225          *
226          * @param session The session received during
227          *                {@link StateCallback#onConfigured(CameraExtensionSession)}
228          * @param request The request that was given to the CameraDevice
229          * @param progress Value between 0 and 100 (inclusive) indicating the current
230          *                post-processing progress
231          *
232          * @see CameraExtensionCharacteristics#isCaptureProcessProgressAvailable
233          *
234          */
onCaptureProcessProgressed(@onNull CameraExtensionSession session, @NonNull CaptureRequest request, @IntRange(from = 0, to = 100) int progress)235         public void onCaptureProcessProgressed(@NonNull CameraExtensionSession session,
236                 @NonNull CaptureRequest request, @IntRange(from = 0, to = 100) int progress) {
237             // default empty implementation
238         }
239     }
240 
241     /**
242      * A callback object for receiving updates about the state of a camera extension session.
243      *
244      */
245     public static abstract class StateCallback {
246 
247         /**
248          * This method is called when the camera device has finished configuring itself, and the
249          * session can start processing capture requests.
250          *
251          * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
252          * be invoked instead of this callback.</p>
253          *
254          * @param session A valid extension session
255          */
onConfigured(@onNull CameraExtensionSession session)256         public abstract void onConfigured(@NonNull CameraExtensionSession session);
257 
258         /**
259          * This method is called if the session cannot be configured as requested.
260          *
261          * <p>This can happen if the set of requested outputs contains unsupported sizes,
262          * too many outputs are requested at once or when trying to initialize multiple
263          * concurrent extension sessions from two (or more) separate camera devices
264          * or the camera device encounters an unrecoverable error during configuration.</p>
265          *
266          * <p>The session is considered to be closed, and all methods called on it after this
267          * callback is invoked will throw an IllegalStateException.</p>
268          *
269          * @param session the session instance that failed to configure
270          */
onConfigureFailed(@onNull CameraExtensionSession session)271         public abstract void onConfigureFailed(@NonNull CameraExtensionSession session);
272 
273         /**
274          * This method is called when the session is closed.
275          *
276          * <p>A session is closed when a new session is created by the parent camera device,
277          * or when the parent camera device is closed (either by the user closing the device,
278          * or due to a camera device disconnection or fatal error).</p>
279          *
280          * <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
281          * any repeating requests are stopped (as if {@link #stopRepeating()} was called).
282          * However, any in-progress capture requests submitted to the session will be completed
283          * as normal.</p>
284          *
285          * @param session the session received during
286          *                {@link StateCallback#onConfigured(CameraExtensionSession)}
287          */
onClosed(@onNull CameraExtensionSession session)288         public void onClosed(@NonNull CameraExtensionSession session) {
289             // default empty implementation
290         }
291     }
292 
293     /**
294      * Get the camera device that this session is created for.
295      */
296     @NonNull
getDevice()297     public android.hardware.camera2.CameraDevice getDevice() {
298         throw new UnsupportedOperationException("Subclasses must override this method");
299     }
300 
301     /**
302      * Submit a request for device-specific processing using input
303      * from the camera device, to produce a single high-quality output result.
304      *
305      * <p>Note that single capture requests currently do not support
306      * client parameters except for controls advertised in
307      * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
308      * The rest of the settings included in the request will be entirely overridden by
309      * the device-specific extension. </p>
310      *
311      * <p> If {@link CameraExtensionCharacteristics#isPostviewAvailable} returns
312      * false, the {@link CaptureRequest.Builder#addTarget} will support only one
313      * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest}
314      * arguments that include further targets will cause IllegalArgumentException to be thrown.
315      * If postview is available, {@link CaptureRequest.Builder#addTarget} will support up to two
316      * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surfaces for the still capture and
317      * postview. IllegalArgumentException will be thrown if a postview target is added without
318      * a still capture target, if more than two target surfaces are added, or if the surface
319      * formats for postview and capture are not equivalent.
320      *
321      * <p>Starting with Android {@link android.os.Build.VERSION_CODES#TIRAMISU} single capture
322      * requests will also support the preview {@link android.graphics.ImageFormat#PRIVATE} target
323      * surface. These can typically be used for enabling AF/AE triggers. Do note, that single
324      * capture requests referencing both output surfaces remain unsupported.</p>
325      *
326      * <p>Each request will produce one new frame for one target Surface, set
327      * with the CaptureRequest builder's
328      * {@link CaptureRequest.Builder#addTarget} method.</p>
329      *
330      * <p>Multiple requests can be in progress at once. Requests are
331      * processed in first-in, first-out order.</p>
332      *
333      * <p>Requests submitted through this method have higher priority than
334      * those submitted through {@link #setRepeatingRequest}, and will be
335      * processed as soon as the current repeat processing completes.</p>
336      *
337      * @param request the settings for this capture
338      * @param executor the executor which will be used for invoking the
339      *                 listener.
340      * @param listener The callback object to notify once this request has
341      *                 been processed.
342      * @return int A unique capture sequence ID used by
343      * {@link ExtensionCaptureCallback#onCaptureSequenceCompleted}.
344      * @throws CameraAccessException    if the camera device is no longer
345      *                                  connected or has encountered a fatal error
346      * @throws IllegalStateException    if this session is no longer active,
347      *                                  either because the session was explicitly closed, a new
348      *                                  session has been created or the camera device has been
349      *                                  closed.
350      * @throws IllegalArgumentException if the request targets no Surfaces
351      *                                  or Surfaces that are not configured as outputs for this
352      *                                  session; or the request targets a set of Surfaces that
353      *                                  cannot be submitted simultaneously.
354      */
capture(@onNull CaptureRequest request, @NonNull Executor executor, @NonNull ExtensionCaptureCallback listener)355     public int capture(@NonNull CaptureRequest request,
356             @NonNull Executor executor,
357             @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
358         throw new UnsupportedOperationException("Subclasses must override this method");
359     }
360 
361     /**
362      * Request endlessly repeating device-specific extension processing of
363      * camera images.
364      *
365      * <p>With this method, the camera device will continually capture images
366      * and process them using the device-specific extension at the maximum
367      * rate possible.</p>
368      *
369      * <p>Note that repeating capture requests currently do not support
370      * client parameters except for controls advertised in
371      * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
372      * The rest of the settings included in the request will be entirely overridden by
373      * the device-specific extension. </p>
374      *
375      * <p>The {@link CaptureRequest.Builder#addTarget} supports only one
376      * target surface. {@link CaptureRequest} arguments that include further
377      * targets will cause IllegalArgumentException to be thrown.</p>
378      *
379      * <p>Repeating requests are a simple way for an application to maintain a
380      * preview or other continuous stream of frames.</p>
381      *
382      * <p>Repeat requests have lower priority than those submitted
383      * through {@link #capture}, so if  {@link #capture} is called when a
384      * repeating request is active, the capture request will be processed
385      * before any further repeating requests are processed.</p>
386      *
387      * <p>To stop the repeating capture, call {@link #stopRepeating}.</p>
388      *
389      * <p>Calling this method will replace any earlier repeating request.</p>
390      *
391      * @param request the request to repeat indefinitely
392      * @param executor the executor which will be used for invoking the
393      *                 listener.
394      * @param listener The callback object to notify every time the
395      *                 request finishes processing.
396      * @return int A unique capture sequence ID used by
397      * {@link ExtensionCaptureCallback#onCaptureSequenceCompleted}.
398      * @throws CameraAccessException    if the camera device is no longer
399      *                                  connected or has encountered a fatal error
400      * @throws IllegalStateException    if this session is no longer active,
401      *                                  either because the session was explicitly closed, a new
402      *                                  session has been created or the camera device has been
403      *                                  closed.
404      * @throws IllegalArgumentException If the request references no
405      *                                  Surfaces or references Surfaces that are not currently
406      *                                  configured as outputs.
407      * @see #capture
408      */
setRepeatingRequest(@onNull CaptureRequest request, @NonNull Executor executor, @NonNull ExtensionCaptureCallback listener)409     public int setRepeatingRequest(@NonNull CaptureRequest request,
410             @NonNull Executor executor,
411             @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
412         throw new UnsupportedOperationException("Subclasses must override this method");
413     }
414 
415     /**
416      * Cancel any ongoing repeating capture set by
417      * {@link #setRepeatingRequest setRepeatingRequest}. Has no effect on
418      * requests submitted through {@link #capture capture}.
419      *
420      * <p>Any currently in-flight captures will still complete.</p>
421      *
422      * @throws CameraAccessException if the camera device is no longer
423      *                               connected or has  encountered a fatal error
424      * @throws IllegalStateException if this session is no longer active,
425      *                               either because the session was explicitly closed, a new
426      *                               session has been created or the camera device has been closed.
427      * @see #setRepeatingRequest
428      */
stopRepeating()429     public void stopRepeating() throws CameraAccessException {
430         throw new UnsupportedOperationException("Subclasses must override this method");
431     }
432 
433     /**
434      * Realtime calculated still {@link #capture} latency.
435      *
436      * @see #getRealtimeStillCaptureLatency()
437      */
438     public final static class StillCaptureLatency {
439         private final long mCaptureLatency, mProcessingLatency;
440 
StillCaptureLatency(long captureLatency, long processingLatency)441         public StillCaptureLatency(long captureLatency, long processingLatency) {
442             mCaptureLatency = captureLatency;
443             mProcessingLatency = processingLatency;
444         }
445         /**
446          * Return the capture latency from
447          * {@link ExtensionCaptureCallback#onCaptureStarted} until
448          * {@link ExtensionCaptureCallback#onCaptureProcessStarted}.
449          *
450          * @return The realtime capture latency in milliseconds.
451          */
getCaptureLatency()452         public long getCaptureLatency() {
453             return mCaptureLatency;
454         }
455 
456         /**
457          * Return the estimated post-processing latency from
458          * {@link ExtensionCaptureCallback#onCaptureProcessStarted} until the processed frame
459          * returns to the client.
460          *
461          * @return returns post-processing latency in milliseconds
462          */
getProcessingLatency()463         public long getProcessingLatency() {
464             return mProcessingLatency;
465         }
466 
467         @Override
equals(Object o)468         public boolean equals(Object o) {
469             if (this == o) return true;
470             if (o == null || getClass() != o.getClass()) return false;
471 
472             StillCaptureLatency latency = (StillCaptureLatency) o;
473 
474             if (mCaptureLatency != latency.mCaptureLatency) return false;
475             if (mProcessingLatency != latency.mProcessingLatency) return false;
476 
477             return true;
478         }
479 
480         @Override
hashCode()481         public int hashCode() {
482             return HashCodeHelpers.hashCode(mCaptureLatency, mProcessingLatency);
483         }
484 
485         @Override
toString()486         public String toString() {
487             return "StillCaptureLatency(processingLatency:" + mProcessingLatency +
488                     ", captureLatency: " + mCaptureLatency + ")";
489         }
490     }
491 
492     /**
493      * Return the realtime still {@link #capture} latency.
494      *
495      * <p>The estimations will take into account the current environment conditions, the camera
496      * state and will include the time spent processing the multi-frame capture request along with
497      * any additional time for encoding of the processed buffer if necessary.</p>
498      *
499      * @return The realtime still capture latency,
500      * or {@code null} if the estimation is not supported.
501      */
502     @Nullable
getRealtimeStillCaptureLatency()503     public StillCaptureLatency getRealtimeStillCaptureLatency() throws CameraAccessException {
504         throw new UnsupportedOperationException("Subclasses must override this method");
505     }
506 
507     /**
508      * Close this capture session asynchronously.
509      *
510      * <p>Closing a session frees up the target output Surfaces of the session
511      * for reuse with either a new session, or to other APIs that can draw
512      * to Surfaces.</p>
513      *
514      * <p>Note that creating a new capture session with
515      * {@link android.hardware.camera2.CameraDevice#createCaptureSession} or
516      * {@link android.hardware.camera2.CameraDevice#createExtensionSession}
517      * will close any existing capture session automatically, and call the
518      * older session listener's {@link StateCallback#onClosed} callback.
519      * Using
520      * {@link android.hardware.camera2.CameraDevice#createCaptureSession} or
521      * {@link android.hardware.camera2.CameraDevice#createExtensionSession}
522      * directly without closing is the recommended approach for quickly
523      * switching to a new session, since unchanged target outputs can be
524      * reused more efficiently.</p>
525      *
526      * <p>Once a session is closed, all methods on it will throw an
527      * IllegalStateException, and any repeating requests are
528      * stopped (as if {@link #stopRepeating()} was called).</p>
529      *
530      * <p>Closing a session is idempotent; closing more than once has no
531      * effect.</p>
532      */
close()533     public void close() throws CameraAccessException {
534         throw new UnsupportedOperationException("Subclasses must override this method");
535     }
536 }
537