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