StreamConfigurationMap.java revision d3b85f69a811113826933c8abf591f20e9b3c8ff
1/*
2 * Copyright (C) 2014 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
17package android.hardware.camera2.params;
18
19import android.graphics.ImageFormat;
20import android.graphics.PixelFormat;
21import android.hardware.camera2.CameraCharacteristics;
22import android.hardware.camera2.CameraDevice;
23import android.hardware.camera2.CaptureRequest;
24import android.hardware.camera2.utils.HashCodeHelpers;
25import android.view.Surface;
26import android.util.Log;
27import android.util.Range;
28import android.util.Size;
29
30import java.util.Arrays;
31import java.util.HashMap;
32import java.util.Objects;
33import java.util.Set;
34
35import static com.android.internal.util.Preconditions.*;
36
37/**
38 * Immutable class to store the available stream
39 * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to set up
40 * {@link android.view.Surface Surfaces} for creating a
41 * {@link android.hardware.camera2.CameraCaptureSession capture session} with
42 * {@link android.hardware.camera2.CameraDevice#createCaptureSession}.
43 * <!-- TODO: link to input stream configuration -->
44 *
45 * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively
46 * for that format) that are supported by a camera device.</p>
47 *
48 * <p>This also contains the minimum frame durations and stall durations for each format/size
49 * combination that can be used to calculate effective frame rate when submitting multiple captures.
50 * </p>
51 *
52 * <p>An instance of this object is available from {@link CameraCharacteristics} using
53 * the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP} key and the
54 * {@link CameraCharacteristics#get} method.</p>
55 *
56 * <pre><code>{@code
57 * CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
58 * StreamConfigurationMap configs = characteristics.get(
59 *         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
60 * }</code></pre>
61 *
62 * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
63 * @see CameraDevice#createCaptureSession
64 */
65public final class StreamConfigurationMap {
66
67    private static final String TAG = "StreamConfigurationMap";
68
69    /**
70     * Create a new {@link StreamConfigurationMap}.
71     *
72     * <p>The array parameters ownership is passed to this object after creation; do not
73     * write to them after this constructor is invoked.</p>
74     *
75     * @param configurations a non-{@code null} array of {@link StreamConfiguration}
76     * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration}
77     * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration}
78     * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if
79     *        camera device does not support high speed video recording
80     *
81     * @throws NullPointerException if any of the arguments except highSpeedVideoConfigurations
82     *         were {@code null} or any subelements were {@code null}
83     *
84     * @hide
85     */
86    public StreamConfigurationMap(
87            StreamConfiguration[] configurations,
88            StreamConfigurationDuration[] minFrameDurations,
89            StreamConfigurationDuration[] stallDurations,
90            HighSpeedVideoConfiguration[] highSpeedVideoConfigurations) {
91
92        mConfigurations = checkArrayElementsNotNull(configurations, "configurations");
93        mMinFrameDurations = checkArrayElementsNotNull(minFrameDurations, "minFrameDurations");
94        mStallDurations = checkArrayElementsNotNull(stallDurations, "stallDurations");
95        if (highSpeedVideoConfigurations == null) {
96            mHighSpeedVideoConfigurations = new HighSpeedVideoConfiguration[0];
97        } else {
98            mHighSpeedVideoConfigurations = checkArrayElementsNotNull(
99                    highSpeedVideoConfigurations, "highSpeedVideoConfigurations");
100        }
101
102        // For each format, track how many sizes there are available to configure
103        for (StreamConfiguration config : configurations) {
104            HashMap<Integer, Integer> map = config.isOutput() ? mOutputFormats : mInputFormats;
105
106            Integer count = map.get(config.getFormat());
107
108            if (count == null) {
109                count = 0;
110            }
111            count = count + 1;
112
113            map.put(config.getFormat(), count);
114        }
115
116        if (!mOutputFormats.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) {
117            throw new AssertionError(
118                    "At least one stream configuration for IMPLEMENTATION_DEFINED must exist");
119        }
120
121        // For each Size/FPS range, track how many FPS range/Size there are available
122        for (HighSpeedVideoConfiguration config : mHighSpeedVideoConfigurations) {
123            Size size = config.getSize();
124            Range<Integer> fpsRange = config.getFpsRange();
125            Integer fpsRangeCount = mHighSpeedVideoSizeMap.get(size);
126            if (fpsRangeCount == null) {
127                fpsRangeCount = 0;
128            }
129            mHighSpeedVideoSizeMap.put(size, fpsRangeCount + 1);
130            Integer sizeCount = mHighSpeedVideoFpsRangeMap.get(fpsRange);
131            if (sizeCount == null) {
132                sizeCount = 0;
133            }
134            mHighSpeedVideoFpsRangeMap.put(fpsRange, sizeCount + 1);
135        }
136    }
137
138    /**
139     * Get the image {@code format} output formats in this stream configuration.
140     *
141     * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
142     * or in {@link PixelFormat} (and there is no possibility of collision).</p>
143     *
144     * <p>Formats listed in this array are guaranteed to return true if queried with
145     * {@link #isOutputSupportedFor(int).</p>
146     *
147     * @return an array of integer format
148     *
149     * @see ImageFormat
150     * @see PixelFormat
151     */
152    public final int[] getOutputFormats() {
153        return getPublicFormats(/*output*/true);
154    }
155
156    /**
157     * Get the image {@code format} input formats in this stream configuration.
158     *
159     * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
160     * or in {@link PixelFormat} (and there is no possibility of collision).</p>
161     *
162     * @return an array of integer format
163     *
164     * @see ImageFormat
165     * @see PixelFormat
166     *
167     * @hide
168     */
169    public final int[] getInputFormats() {
170        return getPublicFormats(/*output*/false);
171    }
172
173    /**
174     * Get the supported input sizes for this input format.
175     *
176     * <p>The format must have come from {@link #getInputFormats}; otherwise
177     * {@code null} is returned.</p>
178     *
179     * @param format a format from {@link #getInputFormats}
180     * @return a non-empty array of sizes, or {@code null} if the format was not available.
181     *
182     * @hide
183     */
184    public Size[] getInputSizes(final int format) {
185        return getPublicFormatSizes(format, /*output*/false);
186    }
187
188    /**
189     * Determine whether or not output surfaces with a particular user-defined format can be passed
190     * {@link CameraDevice#createCaptureSession createCaptureSession}.
191     *
192     * <p>This method determines that the output {@code format} is supported by the camera device;
193     * each output {@code surface} target may or may not itself support that {@code format}.
194     * Refer to the class which provides the surface for additional documentation.</p>
195     *
196     * <p>Formats for which this returns {@code true} are guaranteed to exist in the result
197     * returned by {@link #getOutputSizes}.</p>
198     *
199     * @param format an image format from either {@link ImageFormat} or {@link PixelFormat}
200     * @return
201     *          {@code true} iff using a {@code surface} with this {@code format} will be
202     *          supported with {@link CameraDevice#createCaptureSession}
203     *
204     * @throws IllegalArgumentException
205     *          if the image format was not a defined named constant
206     *          from either {@link ImageFormat} or {@link PixelFormat}
207     *
208     * @see ImageFormat
209     * @see PixelFormat
210     * @see CameraDevice#createCaptureSession
211     */
212    public boolean isOutputSupportedFor(int format) {
213        checkArgumentFormat(format);
214
215        format = imageFormatToInternal(format);
216        return getFormatsMap(/*output*/true).containsKey(format);
217    }
218
219    /**
220     * Determine whether or not output streams can be configured with a particular class
221     * as a consumer.
222     *
223     * <p>The following list is generally usable for outputs:
224     * <ul>
225     * <li>{@link android.media.ImageReader} -
226     * Recommended for image processing or streaming to external resources (such as a file or
227     * network)
228     * <li>{@link android.media.MediaRecorder} -
229     * Recommended for recording video (simple to use)
230     * <li>{@link android.media.MediaCodec} -
231     * Recommended for recording video (more complicated to use, with more flexibility)
232     * <li>{@link android.renderscript.Allocation} -
233     * Recommended for image processing with {@link android.renderscript RenderScript}
234     * <li>{@link android.view.SurfaceHolder} -
235     * Recommended for low-power camera preview with {@link android.view.SurfaceView}
236     * <li>{@link android.graphics.SurfaceTexture} -
237     * Recommended for OpenGL-accelerated preview processing or compositing with
238     * {@link android.view.TextureView}
239     * </ul>
240     * </p>
241     *
242     * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i>
243     * provide a producer endpoint that is suitable to be used with
244     * {@link CameraDevice#createCaptureSession}.</p>
245     *
246     * <p>Since not all of the above classes support output of all format and size combinations,
247     * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p>
248     *
249     * @param klass a non-{@code null} {@link Class} object reference
250     * @return {@code true} if this class is supported as an output, {@code false} otherwise
251     *
252     * @throws NullPointerException if {@code klass} was {@code null}
253     *
254     * @see CameraDevice#createCaptureSession
255     * @see #isOutputSupportedFor(Surface)
256     */
257    public static <T> boolean isOutputSupportedFor(Class<T> klass) {
258        checkNotNull(klass, "klass must not be null");
259
260        if (klass == android.media.ImageReader.class) {
261            return true;
262        } else if (klass == android.media.MediaRecorder.class) {
263            return true;
264        } else if (klass == android.media.MediaCodec.class) {
265            return true;
266        } else if (klass == android.renderscript.Allocation.class) {
267            return true;
268        } else if (klass == android.view.SurfaceHolder.class) {
269            return true;
270        } else if (klass == android.graphics.SurfaceTexture.class) {
271            return true;
272        }
273
274        return false;
275    }
276
277    /**
278     * Determine whether or not the {@code surface} in its current state is suitable to be included
279     * in a {@link CameraDevice#createCaptureSession capture session} as an output.
280     *
281     * <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations
282     * of that {@code surface} are compatible. Some classes that provide the {@code surface} are
283     * compatible with the {@link CameraDevice} in general
284     * (see {@link #isOutputSupportedFor(Class)}, but it is the caller's responsibility to put the
285     * {@code surface} into a state that will be compatible with the {@link CameraDevice}.</p>
286     *
287     * <p>Reasons for a {@code surface} being specifically incompatible might be:
288     * <ul>
289     * <li>Using a format that's not listed by {@link #getOutputFormats}
290     * <li>Using a format/size combination that's not listed by {@link #getOutputSizes}
291     * <li>The {@code surface} itself is not in a state where it can service a new producer.</p>
292     * </li>
293     * </ul>
294     *
295     * This is not an exhaustive list; see the particular class's documentation for further
296     * possible reasons of incompatibility.</p>
297     *
298     * @param surface a non-{@code null} {@link Surface} object reference
299     * @return {@code true} if this is supported, {@code false} otherwise
300     *
301     * @throws NullPointerException if {@code surface} was {@code null}
302     *
303     * @see CameraDevice#createCaptureSession
304     * @see #isOutputSupportedFor(Class)
305     */
306    public boolean isOutputSupportedFor(Surface surface) {
307        checkNotNull(surface, "surface must not be null");
308
309        throw new UnsupportedOperationException("Not implemented yet");
310
311        // TODO: JNI function that checks the Surface's IGraphicBufferProducer state
312    }
313
314    /**
315     * Get a list of sizes compatible with {@code klass} to use as an output.
316     *
317     * <p>Since some of the supported classes may support additional formats beyond
318     * an opaque/implementation-defined (under-the-hood) format; this function only returns
319     * sizes for the implementation-defined format.</p>
320     *
321     * <p>Some classes such as {@link android.media.ImageReader} may only support user-defined
322     * formats; in particular {@link #isOutputSupportedFor(Class)} will return {@code true} for
323     * that class and this method will return an empty array (but not {@code null}).</p>
324     *
325     * <p>If a well-defined format such as {@code NV21} is required, use
326     * {@link #getOutputSizes(int)} instead.</p>
327     *
328     * <p>The {@code klass} should be a supported output, that querying
329     * {@code #isOutputSupportedFor(Class)} should return {@code true}.</p>
330     *
331     * @param klass
332     *          a non-{@code null} {@link Class} object reference
333     * @return
334     *          an array of supported sizes for implementation-defined formats,
335     *          or {@code null} iff the {@code klass} is not a supported output
336     *
337     * @throws NullPointerException if {@code klass} was {@code null}
338     *
339     * @see #isOutputSupportedFor(Class)
340     */
341    public <T> Size[] getOutputSizes(Class<T> klass) {
342        if (isOutputSupportedFor(klass) == false) {
343            return null;
344        }
345
346        return getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, /*output*/true);
347    }
348
349    /**
350     * Get a list of sizes compatible with the requested image {@code format}.
351     *
352     * <p>The {@code format} should be a supported format (one of the formats returned by
353     * {@link #getOutputFormats}).</p>
354     *
355     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
356     * @return
357     *          an array of supported sizes,
358     *          or {@code null} if the {@code format} is not a supported output
359     *
360     * @see ImageFormat
361     * @see PixelFormat
362     * @see #getOutputFormats
363     */
364    public Size[] getOutputSizes(int format) {
365        return getPublicFormatSizes(format, /*output*/true);
366    }
367
368    /**
369     * Get a list of supported high speed video recording sizes.
370     *
371     * <p> When HIGH_SPEED_VIDEO is supported in
372     * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES available scene modes}, this
373     * method will list the supported high speed video size configurations. All the sizes listed
374     * will be a subset of the sizes reported by {@link #getOutputSizes} for processed non-stalling
375     * formats (typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12)</p>
376     *
377     * <p> To enable high speed video recording, application must set
378     * {@link CaptureRequest#CONTROL_SCENE_MODE} to
379     * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture
380     * requests and select the video size from this method and
381     * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from
382     * {@link #getHighSpeedVideoFpsRangesFor} to configure the recording and preview streams and
383     * setup the recording requests. For example, if the application intends to do high speed
384     * recording, it can select the maximum size reported by this method to configure output
385     * streams. Note that for the use case of multiple output streams, application must select one
386     * unique size from this method to use. Otherwise a request error might occur. Once the size is
387     * selected, application can get the supported FPS ranges by
388     * {@link #getHighSpeedVideoFpsRangesFor}, and use these FPS ranges to setup the recording
389     * requests.</p>
390     *
391     * @return
392     *          an array of supported high speed video recording sizes
393     *
394     * @see #getHighSpeedVideoFpsRangesFor(Size)
395     */
396    public Size[] getHighSpeedVideoSizes() {
397        Set<Size> keySet = mHighSpeedVideoSizeMap.keySet();
398        return keySet.toArray(new Size[keySet.size()]);
399    }
400
401    /**
402     * Get the frame per second ranges (fpsMin, fpsMax) for input high speed video size.
403     *
404     * <p> See {@link #getHighSpeedVideoSizes} for how to enable high speed recording.</p>
405     *
406     * <p> For normal video recording use case, where some application will NOT set
407     * {@link CaptureRequest#CONTROL_SCENE_MODE} to
408     * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture
409     * requests, the {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS ranges} reported in
410     * this method must not be used to setup capture requests, or it will cause request error.</p>
411     *
412     * @param size one of the sizes returned by {@link #getHighSpeedVideoSizes()}
413     * @return
414     *          An array of FPS range to use with
415     *          {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE TARGET_FPS_RANGE} when using
416     *          {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} scene
417     *          mode.
418     *          The upper bound of returned ranges is guaranteed to be larger or equal to 60.
419     *
420     * @throws IllegalArgumentException if input size does not exist in the return value of
421     *         getHighSpeedVideoSizes
422     * @see #getHighSpeedVideoSizes()
423     */
424    public Range<Integer>[] getHighSpeedVideoFpsRangesFor(Size size) {
425        Integer fpsRangeCount = mHighSpeedVideoSizeMap.get(size);
426        if (fpsRangeCount == null || fpsRangeCount == 0) {
427            throw new IllegalArgumentException(String.format(
428                    "Size %s does not support high speed video recording", size));
429        }
430
431        @SuppressWarnings("unchecked")
432        Range<Integer>[] fpsRanges = new Range[fpsRangeCount];
433        int i = 0;
434        for (HighSpeedVideoConfiguration config : mHighSpeedVideoConfigurations) {
435            if (size.equals(config.getSize())) {
436                fpsRanges[i++] = config.getFpsRange();
437            }
438        }
439        return fpsRanges;
440    }
441
442    /**
443     * Get a list of supported high speed video recording FPS ranges.
444     *
445     * <p> When HIGH_SPEED_VIDEO is supported in
446     * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES available scene modes}, this
447     * method will list the supported high speed video FPS range configurations. Application can
448     * then use {@link #getHighSpeedVideoSizesFor} to query available sizes for one of returned
449     * FPS range.</p>
450     *
451     * <p> To enable high speed video recording, application must set
452     * {@link CaptureRequest#CONTROL_SCENE_MODE} to
453     * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture
454     * requests and select the video size from {@link #getHighSpeedVideoSizesFor} and
455     * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from
456     * this method to configure the recording and preview streams and setup the recording requests.
457     * For example, if the application intends to do high speed recording, it can select one FPS
458     * range reported by this method, query the video sizes corresponding to this FPS range  by
459     * {@link #getHighSpeedVideoSizesFor} and select one of reported sizes to configure output
460     * streams. Note that for the use case of multiple output streams, application must select one
461     * unique size from {@link #getHighSpeedVideoSizesFor}, and use it for all output streams.
462     * Otherwise a request error might occur when attempting to enable
463     * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO}.
464     * Once the stream is configured, application can set the FPS range in the recording requests.
465     * </p>
466     *
467     * @return
468     *          an array of supported high speed video recording FPS ranges
469     *          The upper bound of returned ranges is guaranteed to be larger or equal to 60.
470     *
471     * @see #getHighSpeedVideoSizesFor
472     */
473    @SuppressWarnings("unchecked")
474    public Range<Integer>[] getHighSpeedVideoFpsRanges() {
475        Set<Range<Integer>> keySet = mHighSpeedVideoFpsRangeMap.keySet();
476        return keySet.toArray(new Range[keySet.size()]);
477    }
478
479    /**
480     * Get the supported video sizes for input FPS range.
481     *
482     * <p> See {@link #getHighSpeedVideoFpsRanges} for how to enable high speed recording.</p>
483     *
484     * <p> For normal video recording use case, where the application will NOT set
485     * {@link CaptureRequest#CONTROL_SCENE_MODE} to
486     * {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} in capture
487     * requests, the {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS ranges} reported in
488     * this method must not be used to setup capture requests, or it will cause request error.</p>
489     *
490     * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
491     * @return
492     *          An array of video sizes to configure output stream when using
493     *          {@link CaptureRequest#CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO HIGH_SPEED_VIDEO} scene
494     *          mode.
495     *
496     * @throws IllegalArgumentException if input FPS range does not exist in the return value of
497     *         getHighSpeedVideoFpsRanges
498     * @see #getHighSpeedVideoFpsRanges()
499     */
500    public Size[] getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
501        Integer sizeCount = mHighSpeedVideoFpsRangeMap.get(fpsRange);
502        if (sizeCount == null || sizeCount == 0) {
503            throw new IllegalArgumentException(String.format(
504                    "FpsRange %s does not support high speed video recording", fpsRange));
505        }
506
507        Size[] sizes = new Size[sizeCount];
508        int i = 0;
509        for (HighSpeedVideoConfiguration config : mHighSpeedVideoConfigurations) {
510            if (fpsRange.equals(config.getFpsRange())) {
511                sizes[i++] = config.getSize();
512            }
513        }
514        return sizes;
515    }
516
517    /**
518     * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
519     * for the format/size combination (in nanoseconds).
520     *
521     * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
522     * <p>{@code size} should be one of the ones returned by
523     * {@link #getOutputSizes(int)}.</p>
524     *
525     * <p>This should correspond to the frame duration when only that stream is active, with all
526     * processing (typically in {@code android.*.mode}) set to either {@code OFF} or {@code FAST}.
527     * </p>
528     *
529     * <p>When multiple streams are used in a request, the minimum frame duration will be
530     * {@code max(individual stream min durations)}.</p>
531     *
532     * <p>For devices that do not support manual sensor control
533     * ({@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR}),
534     * this function may return 0.</p>
535     *
536     * <!--
537     * TODO: uncomment after adding input stream support
538     * <p>The minimum frame duration of a stream (of a particular format, size) is the same
539     * regardless of whether the stream is input or output.</p>
540     * -->
541     *
542     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
543     * @param size an output-compatible size
544     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
545     *          0 if the minimum frame duration is not available.
546     *
547     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
548     * @throws NullPointerException if {@code size} was {@code null}
549     *
550     * @see CaptureRequest#SENSOR_FRAME_DURATION
551     * @see #getOutputStallDuration(int, Size)
552     * @see ImageFormat
553     * @see PixelFormat
554     */
555    public long getOutputMinFrameDuration(int format, Size size) {
556        checkNotNull(size, "size must not be null");
557        checkArgumentFormatSupported(format, /*output*/true);
558
559        return getInternalFormatDuration(imageFormatToInternal(format), size, DURATION_MIN_FRAME);
560    }
561
562    /**
563     * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
564     * for the class/size combination (in nanoseconds).
565     *
566     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
567     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
568     *
569     * <p>{@code klass} should be one of the ones which is supported by
570     * {@link #isOutputSupportedFor(Class)}.</p>
571     *
572     * <p>{@code size} should be one of the ones returned by
573     * {@link #getOutputSizes(int)}.</p>
574     *
575     * <p>This should correspond to the frame duration when only that stream is active, with all
576     * processing (typically in {@code android.*.mode}) set to either {@code OFF} or {@code FAST}.
577     * </p>
578     *
579     * <p>When multiple streams are used in a request, the minimum frame duration will be
580     * {@code max(individual stream min durations)}.</p>
581     *
582     * <p>For devices that do not support manual sensor control
583     * ({@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR}),
584     * this function may return 0.</p>
585     *
586     * <!--
587     * TODO: uncomment after adding input stream support
588     * <p>The minimum frame duration of a stream (of a particular format, size) is the same
589     * regardless of whether the stream is input or output.</p>
590     * -->
591     *
592     * @param klass
593     *          a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
594     *          non-empty array returned by {@link #getOutputSizes(Class)}
595     * @param size an output-compatible size
596     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
597     *          0 if the minimum frame duration is not available.
598     *
599     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
600     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
601     *
602     * @see CaptureRequest#SENSOR_FRAME_DURATION
603     * @see ImageFormat
604     * @see PixelFormat
605     */
606    public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
607        if (!isOutputSupportedFor(klass)) {
608            throw new IllegalArgumentException("klass was not supported");
609        }
610
611        return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
612                size, DURATION_MIN_FRAME);
613    }
614
615    /**
616     * Get the stall duration for the format/size combination (in nanoseconds).
617     *
618     * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
619     * <p>{@code size} should be one of the ones returned by
620     * {@link #getOutputSizes(int)}.</p>
621     *
622     * <p>
623     * A stall duration is how much extra time would get added to the normal minimum frame duration
624     * for a repeating request that has streams with non-zero stall.
625     *
626     * <p>For example, consider JPEG captures which have the following characteristics:
627     *
628     * <ul>
629     * <li>JPEG streams act like processed YUV streams in requests for which they are not included;
630     * in requests in which they are directly referenced, they act as JPEG streams.
631     * This is because supporting a JPEG stream requires the underlying YUV data to always be ready
632     * for use by a JPEG encoder, but the encoder will only be used (and impact frame duration) on
633     * requests that actually reference a JPEG stream.
634     * <li>The JPEG processor can run concurrently to the rest of the camera pipeline, but cannot
635     * process more than 1 capture at a time.
636     * </ul>
637     *
638     * <p>In other words, using a repeating YUV request would result in a steady frame rate
639     * (let's say it's 30 FPS). If a single JPEG request is submitted periodically,
640     * the frame rate will stay at 30 FPS (as long as we wait for the previous JPEG to return each
641     * time). If we try to submit a repeating YUV + JPEG request, then the frame rate will drop from
642     * 30 FPS.</p>
643     *
644     * <p>In general, submitting a new request with a non-0 stall time stream will <em>not</em> cause a
645     * frame rate drop unless there are still outstanding buffers for that stream from previous
646     * requests.</p>
647     *
648     * <p>Submitting a repeating request with streams (call this {@code S}) is the same as setting
649     * the minimum frame duration from the normal minimum frame duration corresponding to {@code S},
650     * added with the maximum stall duration for {@code S}.</p>
651     *
652     * <p>If interleaving requests with and without a stall duration, a request will stall by the
653     * maximum of the remaining times for each can-stall stream with outstanding buffers.</p>
654     *
655     * <p>This means that a stalling request will not have an exposure start until the stall has
656     * completed.</p>
657     *
658     * <p>This should correspond to the stall duration when only that stream is active, with all
659     * processing (typically in {@code android.*.mode}) set to {@code FAST} or {@code OFF}.
660     * Setting any of the processing modes to {@code HIGH_QUALITY} effectively results in an
661     * indeterminate stall duration for all streams in a request (the regular stall calculation
662     * rules are ignored).</p>
663     *
664     * <p>The following formats may always have a stall duration:
665     * <ul>
666     * <li>{@link ImageFormat#JPEG JPEG}
667     * <li>{@link ImageFormat#RAW_SENSOR RAW16}
668     * </ul>
669     * </p>
670     *
671     * <p>The following formats will never have a stall duration:
672     * <ul>
673     * <li>{@link ImageFormat#YUV_420_888 YUV_420_888}
674     * <li>{@link #isOutputSupportedFor(Class) Implementation-Defined}
675     * </ul></p>
676     *
677     * <p>
678     * All other formats may or may not have an allowed stall duration on a per-capability basis;
679     * refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
680     * android.request.availableCapabilities} for more details.</p>
681     * </p>
682     *
683     * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}
684     * for more information about calculating the max frame rate (absent stalls).</p>
685     *
686     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
687     * @param size an output-compatible size
688     * @return a stall duration {@code >=} 0 in nanoseconds
689     *
690     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
691     * @throws NullPointerException if {@code size} was {@code null}
692     *
693     * @see CaptureRequest#SENSOR_FRAME_DURATION
694     * @see ImageFormat
695     * @see PixelFormat
696     */
697    public long getOutputStallDuration(int format, Size size) {
698        checkArgumentFormatSupported(format, /*output*/true);
699
700        return getInternalFormatDuration(imageFormatToInternal(format),
701                size, DURATION_STALL);
702    }
703
704    /**
705     * Get the stall duration for the class/size combination (in nanoseconds).
706     *
707     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
708     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
709     *
710     * <p>{@code klass} should be one of the ones with a non-empty array returned by
711     * {@link #getOutputSizes(Class)}.</p>
712     *
713     * <p>{@code size} should be one of the ones returned by
714     * {@link #getOutputSizes(Class)}.</p>
715     *
716     * <p>See {@link #getOutputStallDuration(int, Size)} for a definition of a
717     * <em>stall duration</em>.</p>
718     *
719     * @param klass
720     *          a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
721     *          non-empty array returned by {@link #getOutputSizes(Class)}
722     * @param size an output-compatible size
723     * @return a minimum frame duration {@code >=} 0 in nanoseconds
724     *
725     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
726     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
727     *
728     * @see CaptureRequest#SENSOR_FRAME_DURATION
729     * @see ImageFormat
730     * @see PixelFormat
731     */
732    public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
733        if (!isOutputSupportedFor(klass)) {
734            throw new IllegalArgumentException("klass was not supported");
735        }
736
737        return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
738                size, DURATION_STALL);
739    }
740
741    /**
742     * Check if this {@link StreamConfigurationMap} is equal to another
743     * {@link StreamConfigurationMap}.
744     *
745     * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
746     *
747     * @return {@code true} if the objects were equal, {@code false} otherwise
748     */
749    @Override
750    public boolean equals(final Object obj) {
751        if (obj == null) {
752            return false;
753        }
754        if (this == obj) {
755            return true;
756        }
757        if (obj instanceof StreamConfigurationMap) {
758            final StreamConfigurationMap other = (StreamConfigurationMap) obj;
759            // XX: do we care about order?
760            return Arrays.equals(mConfigurations, other.mConfigurations) &&
761                    Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) &&
762                    Arrays.equals(mStallDurations, other.mStallDurations) &&
763                    Arrays.equals(mHighSpeedVideoConfigurations,
764                            other.mHighSpeedVideoConfigurations);
765        }
766        return false;
767    }
768
769    /**
770     * {@inheritDoc}
771     */
772    @Override
773    public int hashCode() {
774        // XX: do we care about order?
775        return HashCodeHelpers.hashCode(
776                mConfigurations, mMinFrameDurations,
777                mStallDurations, mHighSpeedVideoConfigurations);
778    }
779
780    // Check that the argument is supported by #getOutputFormats or #getInputFormats
781    private int checkArgumentFormatSupported(int format, boolean output) {
782        checkArgumentFormat(format);
783
784        int[] formats = output ? getOutputFormats() : getInputFormats();
785        for (int i = 0; i < formats.length; ++i) {
786            if (format == formats[i]) {
787                return format;
788            }
789        }
790
791        throw new IllegalArgumentException(String.format(
792                "format %x is not supported by this stream configuration map", format));
793    }
794
795    /**
796     * Ensures that the format is either user-defined or implementation defined.
797     *
798     * <p>If a format has a different internal representation than the public representation,
799     * passing in the public representation here will fail.</p>
800     *
801     * <p>For example if trying to use {@link ImageFormat#JPEG}:
802     * it has a different public representation than the internal representation
803     * {@code HAL_PIXEL_FORMAT_BLOB}, this check will fail.</p>
804     *
805     * <p>Any invalid/undefined formats will raise an exception.</p>
806     *
807     * @param format image format
808     * @return the format
809     *
810     * @throws IllegalArgumentException if the format was invalid
811     */
812    static int checkArgumentFormatInternal(int format) {
813        switch (format) {
814            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
815            case HAL_PIXEL_FORMAT_BLOB:
816            case HAL_PIXEL_FORMAT_RAW_OPAQUE:
817                return format;
818            case ImageFormat.JPEG:
819                throw new IllegalArgumentException(
820                        "ImageFormat.JPEG is an unknown internal format");
821            default:
822                return checkArgumentFormat(format);
823        }
824    }
825
826    /**
827     * Ensures that the format is publicly user-defined in either ImageFormat or PixelFormat.
828     *
829     * <p>If a format has a different public representation than the internal representation,
830     * passing in the internal representation here will fail.</p>
831     *
832     * <p>For example if trying to use {@code HAL_PIXEL_FORMAT_BLOB}:
833     * it has a different internal representation than the public representation
834     * {@link ImageFormat#JPEG}, this check will fail.</p>
835     *
836     * <p>Any invalid/undefined formats will raise an exception, including implementation-defined.
837     * </p>
838     *
839     * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p>
840     *
841     * @param format image format
842     * @return the format
843     *
844     * @throws IllegalArgumentException if the format was not user-defined
845     */
846    static int checkArgumentFormat(int format) {
847        if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
848            throw new IllegalArgumentException(String.format(
849                    "format 0x%x was not defined in either ImageFormat or PixelFormat", format));
850        }
851
852        return format;
853    }
854
855    /**
856     * Convert a public-visible {@code ImageFormat} into an internal format
857     * compatible with {@code graphics.h}.
858     *
859     * <p>In particular these formats are converted:
860     * <ul>
861     * <li>HAL_PIXEL_FORMAT_BLOB => ImageFormat.JPEG
862     * </ul>
863     * </p>
864     *
865     * <p>Passing in an implementation-defined format which has no public equivalent will fail;
866     * as will passing in a public format which has a different internal format equivalent.
867     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
868     *
869     * <p>All other formats are returned as-is, no further invalid check is performed.</p>
870     *
871     * <p>This function is the dual of {@link #imageFormatToInternal}.</p>
872     *
873     * @param format image format from {@link ImageFormat} or {@link PixelFormat}
874     * @return the converted image formats
875     *
876     * @throws IllegalArgumentException
877     *          if {@code format} is {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED} or
878     *          {@link ImageFormat#JPEG}
879     *
880     * @see ImageFormat
881     * @see PixelFormat
882     * @see #checkArgumentFormat
883     */
884    static int imageFormatToPublic(int format) {
885        switch (format) {
886            case HAL_PIXEL_FORMAT_BLOB:
887                return ImageFormat.JPEG;
888            case ImageFormat.JPEG:
889                throw new IllegalArgumentException(
890                        "ImageFormat.JPEG is an unknown internal format");
891            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
892                throw new IllegalArgumentException(
893                        "IMPLEMENTATION_DEFINED must not leak to public API");
894            default:
895                return format;
896        }
897    }
898
899    /**
900     * Convert image formats from internal to public formats (in-place).
901     *
902     * @param formats an array of image formats
903     * @return {@code formats}
904     *
905     * @see #imageFormatToPublic
906     */
907    static int[] imageFormatToPublic(int[] formats) {
908        if (formats == null) {
909            return null;
910        }
911
912        for (int i = 0; i < formats.length; ++i) {
913            formats[i] = imageFormatToPublic(formats[i]);
914        }
915
916        return formats;
917    }
918
919    /**
920     * Convert a public format compatible with {@code ImageFormat} to an internal format
921     * from {@code graphics.h}.
922     *
923     * <p>In particular these formats are converted:
924     * <ul>
925     * <li>ImageFormat.JPEG => HAL_PIXEL_FORMAT_BLOB
926     * </ul>
927     * </p>
928     *
929     * <p>Passing in an implementation-defined format here will fail (it's not a public format);
930     * as will passing in an internal format which has a different public format equivalent.
931     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
932     *
933     * <p>All other formats are returned as-is, no invalid check is performed.</p>
934     *
935     * <p>This function is the dual of {@link #imageFormatToPublic}.</p>
936     *
937     * @param format public image format from {@link ImageFormat} or {@link PixelFormat}
938     * @return the converted image formats
939     *
940     * @see ImageFormat
941     * @see PixelFormat
942     *
943     * @throws IllegalArgumentException
944     *              if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
945     */
946    static int imageFormatToInternal(int format) {
947        switch (format) {
948            case ImageFormat.JPEG:
949                return HAL_PIXEL_FORMAT_BLOB;
950            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
951                throw new IllegalArgumentException(
952                        "IMPLEMENTATION_DEFINED is not allowed via public API");
953            default:
954                return format;
955        }
956    }
957
958    /**
959     * Convert image formats from public to internal formats (in-place).
960     *
961     * @param formats an array of image formats
962     * @return {@code formats}
963     *
964     * @see #imageFormatToInternal
965     *
966     * @hide
967     */
968    public static int[] imageFormatToInternal(int[] formats) {
969        if (formats == null) {
970            return null;
971        }
972
973        for (int i = 0; i < formats.length; ++i) {
974            formats[i] = imageFormatToInternal(formats[i]);
975        }
976
977        return formats;
978    }
979
980    private Size[] getPublicFormatSizes(int format, boolean output) {
981        try {
982            checkArgumentFormatSupported(format, output);
983        } catch (IllegalArgumentException e) {
984            return null;
985        }
986
987        format = imageFormatToInternal(format);
988
989        return getInternalFormatSizes(format, output);
990    }
991
992    private Size[] getInternalFormatSizes(int format, boolean output) {
993        HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
994
995        Integer sizesCount = formatsMap.get(format);
996        if (sizesCount == null) {
997            throw new IllegalArgumentException("format not available");
998        }
999
1000        int len = sizesCount;
1001        Size[] sizes = new Size[len];
1002        int sizeIndex = 0;
1003
1004        for (StreamConfiguration config : mConfigurations) {
1005            if (config.getFormat() == format && config.isOutput() == output) {
1006                sizes[sizeIndex++] = config.getSize();
1007            }
1008        }
1009
1010        if (sizeIndex != len) {
1011            throw new AssertionError(
1012                    "Too few sizes (expected " + len + ", actual " + sizeIndex + ")");
1013        }
1014
1015        return sizes;
1016    }
1017
1018    /** Get the list of publically visible output formats; does not include IMPL_DEFINED */
1019    private int[] getPublicFormats(boolean output) {
1020        int[] formats = new int[getPublicFormatCount(output)];
1021
1022        int i = 0;
1023
1024        for (int format : getFormatsMap(output).keySet()) {
1025            if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
1026                formats[i++] = format;
1027            }
1028        }
1029
1030        if (formats.length != i) {
1031            throw new AssertionError("Too few formats " + i + ", expected " + formats.length);
1032        }
1033
1034        return imageFormatToPublic(formats);
1035    }
1036
1037    /** Get the format -> size count map for either output or input formats */
1038    private HashMap<Integer, Integer> getFormatsMap(boolean output) {
1039        return output ? mOutputFormats : mInputFormats;
1040    }
1041
1042    private long getInternalFormatDuration(int format, Size size, int duration) {
1043        // assume format is already checked, since its internal
1044
1045        if (!arrayContains(getInternalFormatSizes(format, /*output*/true), size)) {
1046            throw new IllegalArgumentException("size was not supported");
1047        }
1048
1049        StreamConfigurationDuration[] durations = getDurations(duration);
1050
1051        for (StreamConfigurationDuration configurationDuration : durations) {
1052            if (configurationDuration.getFormat() == format &&
1053                    configurationDuration.getWidth() == size.getWidth() &&
1054                    configurationDuration.getHeight() == size.getHeight()) {
1055                return configurationDuration.getDuration();
1056            }
1057        }
1058        // Default duration is '0' (unsupported/no extra stall)
1059        return 0;
1060    }
1061
1062    /**
1063     * Get the durations array for the kind of duration
1064     *
1065     * @see #DURATION_MIN_FRAME
1066     * @see #DURATION_STALL
1067     * */
1068    private StreamConfigurationDuration[] getDurations(int duration) {
1069        switch (duration) {
1070            case DURATION_MIN_FRAME:
1071                return mMinFrameDurations;
1072            case DURATION_STALL:
1073                return mStallDurations;
1074            default:
1075                throw new IllegalArgumentException("duration was invalid");
1076        }
1077    }
1078
1079    /** Count the number of publicly-visible output formats */
1080    private int getPublicFormatCount(boolean output) {
1081        HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
1082
1083        int size = formatsMap.size();
1084        if (formatsMap.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) {
1085            size -= 1;
1086        }
1087        return size;
1088    }
1089
1090    private static <T> boolean arrayContains(T[] array, T element) {
1091        if (array == null) {
1092            return false;
1093        }
1094
1095        for (T el : array) {
1096            if (Objects.equals(el, element)) {
1097                return true;
1098            }
1099        }
1100
1101        return false;
1102    }
1103
1104    // from system/core/include/system/graphics.h
1105    private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
1106    private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
1107    private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24;
1108
1109    /**
1110     * @see #getDurations(int)
1111     * @see #getDurationDefault(int)
1112     */
1113    private static final int DURATION_MIN_FRAME = 0;
1114    private static final int DURATION_STALL = 1;
1115
1116    private final StreamConfiguration[] mConfigurations;
1117    private final StreamConfigurationDuration[] mMinFrameDurations;
1118    private final StreamConfigurationDuration[] mStallDurations;
1119    private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations;
1120
1121    /** ImageFormat -> num output sizes mapping */
1122    private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mOutputFormats =
1123            new HashMap<Integer, Integer>();
1124    /** ImageFormat -> num input sizes mapping */
1125    private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mInputFormats =
1126            new HashMap<Integer, Integer>();
1127    /** High speed video Size -> FPS range count mapping*/
1128    private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap =
1129            new HashMap<Size, Integer>();
1130    /** High speed video FPS range -> Size count mapping*/
1131    private final HashMap</*HighSpeedVideoFpsRange*/Range<Integer>, /*Count*/Integer>
1132            mHighSpeedVideoFpsRangeMap = new HashMap<Range<Integer>, Integer>();
1133
1134}
1135