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