StreamConfigurationMap.java revision 3e4fed203fe7c945c53c6d6bb9f160932a1d15b3
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     * <!--
361     * TODO: uncomment after adding input stream support
362     * <p>The minimum frame duration of a stream (of a particular format, size) is the same
363     * regardless of whether the stream is input or output.</p>
364     * -->
365     *
366     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
367     * @param size an output-compatible size
368     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
369     *          {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
370     *          can only occur on limited mode devices).
371     *
372     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
373     * @throws NullPointerException if {@code size} was {@code null}
374     *
375     * @see CaptureRequest#SENSOR_FRAME_DURATION
376     * @see #getOutputStallDuration(int, Size)
377     * @see ImageFormat
378     * @see PixelFormat
379     */
380    public long getOutputMinFrameDuration(int format, Size size) {
381        checkNotNull(size, "size must not be null");
382        checkArgumentFormatSupported(format, /*output*/true);
383
384        return getInternalFormatDuration(imageFormatToInternal(format), size, DURATION_MIN_FRAME);
385    }
386
387    /**
388     * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
389     * for the class/size combination (in nanoseconds).
390     *
391     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
392     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
393     *
394     * <p>{@code klass} should be one of the ones which is supported by
395     * {@link #isOutputSupportedFor(Class)}.</p>
396     *
397     * <p>{@code size} should be one of the ones returned by
398     * {@link #getOutputSizes(int)}.</p>
399     *
400     * <p>This should correspond to the frame duration when only that stream is active, with all
401     * processing (typically in {@code android.*.mode}) set to either {@code OFF} or {@code FAST}.
402     * </p>
403     *
404     * <p>When multiple streams are used in a request, the minimum frame duration will be
405     * {@code max(individual stream min durations)}.</p>
406     *
407     * <!--
408     * TODO: uncomment after adding input stream support
409     * <p>The minimum frame duration of a stream (of a particular format, size) is the same
410     * regardless of whether the stream is input or output.</p>
411     * -->
412     *
413     * @param klass
414     *          a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
415     *          non-empty array returned by {@link #getOutputSizes(Class)}
416     * @param size an output-compatible size
417     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
418     *          {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
419     *          can only occur on limited mode devices).
420     *
421     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
422     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
423     *
424     * @see CaptureRequest#SENSOR_FRAME_DURATION
425     * @see ImageFormat
426     * @see PixelFormat
427     */
428    public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
429        if (!isOutputSupportedFor(klass)) {
430            throw new IllegalArgumentException("klass was not supported");
431        }
432
433        return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
434                size, DURATION_MIN_FRAME);
435    }
436
437    /**
438     * Get the stall duration for the format/size combination (in nanoseconds).
439     *
440     * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
441     * <p>{@code size} should be one of the ones returned by
442     * {@link #getOutputSizes(int)}.</p>
443     *
444     * <p>
445     * A stall duration is how much extra time would get added to the normal minimum frame duration
446     * for a repeating request that has streams with non-zero stall.
447     *
448     * <p>For example, consider JPEG captures which have the following characteristics:
449     *
450     * <ul>
451     * <li>JPEG streams act like processed YUV streams in requests for which they are not included;
452     * in requests in which they are directly referenced, they act as JPEG streams.
453     * This is because supporting a JPEG stream requires the underlying YUV data to always be ready
454     * for use by a JPEG encoder, but the encoder will only be used (and impact frame duration) on
455     * requests that actually reference a JPEG stream.
456     * <li>The JPEG processor can run concurrently to the rest of the camera pipeline, but cannot
457     * process more than 1 capture at a time.
458     * </ul>
459     *
460     * <p>In other words, using a repeating YUV request would result in a steady frame rate
461     * (let's say it's 30 FPS). If a single JPEG request is submitted periodically,
462     * the frame rate will stay at 30 FPS (as long as we wait for the previous JPEG to return each
463     * time). If we try to submit a repeating YUV + JPEG request, then the frame rate will drop from
464     * 30 FPS.</p>
465     *
466     * <p>In general, submitting a new request with a non-0 stall time stream will <em>not</em> cause a
467     * frame rate drop unless there are still outstanding buffers for that stream from previous
468     * requests.</p>
469     *
470     * <p>Submitting a repeating request with streams (call this {@code S}) is the same as setting
471     * the minimum frame duration from the normal minimum frame duration corresponding to {@code S},
472     * added with the maximum stall duration for {@code S}.</p>
473     *
474     * <p>If interleaving requests with and without a stall duration, a request will stall by the
475     * maximum of the remaining times for each can-stall stream with outstanding buffers.</p>
476     *
477     * <p>This means that a stalling request will not have an exposure start until the stall has
478     * completed.</p>
479     *
480     * <p>This should correspond to the stall duration when only that stream is active, with all
481     * processing (typically in {@code android.*.mode}) set to {@code FAST} or {@code OFF}.
482     * Setting any of the processing modes to {@code HIGH_QUALITY} effectively results in an
483     * indeterminate stall duration for all streams in a request (the regular stall calculation
484     * rules are ignored).</p>
485     *
486     * <p>The following formats may always have a stall duration:
487     * <ul>
488     * <li>{@link ImageFormat#JPEG JPEG}
489     * <li>{@link ImageFormat#RAW_SENSOR RAW16}
490     * </ul>
491     * </p>
492     *
493     * <p>The following formats will never have a stall duration:
494     * <ul>
495     * <li>{@link ImageFormat#YUV_420_888 YUV_420_888}
496     * <li>{@link #isOutputSupportedFor(Class) Implementation-Defined}
497     * </ul></p>
498     *
499     * <p>
500     * All other formats may or may not have an allowed stall duration on a per-capability basis;
501     * refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
502     * android.request.availableCapabilities} for more details.</p>
503     * </p>
504     *
505     * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}
506     * for more information about calculating the max frame rate (absent stalls).</p>
507     *
508     * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
509     * @param size an output-compatible size
510     * @return a stall duration {@code >=} 0 in nanoseconds
511     *
512     * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
513     * @throws NullPointerException if {@code size} was {@code null}
514     *
515     * @see CaptureRequest#SENSOR_FRAME_DURATION
516     * @see ImageFormat
517     * @see PixelFormat
518     */
519    public long getOutputStallDuration(int format, Size size) {
520        checkArgumentFormatSupported(format, /*output*/true);
521
522        return getInternalFormatDuration(imageFormatToInternal(format),
523                size, DURATION_STALL);
524    }
525
526    /**
527     * Get the stall duration for the class/size combination (in nanoseconds).
528     *
529     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
530     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
531     *
532     * <p>{@code klass} should be one of the ones with a non-empty array returned by
533     * {@link #getOutputSizes(Class)}.</p>
534     *
535     * <p>{@code size} should be one of the ones returned by
536     * {@link #getOutputSizes(Class)}.</p>
537     *
538     * <p>See {@link #getOutputStallDuration(int, Size)} for a definition of a
539     * <em>stall duration</em>.</p>
540     *
541     * @param klass
542     *          a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
543     *          non-empty array returned by {@link #getOutputSizes(Class)}
544     * @param size an output-compatible size
545     * @return a minimum frame duration {@code >=} 0 in nanoseconds
546     *
547     * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
548     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
549     *
550     * @see CaptureRequest#SENSOR_FRAME_DURATION
551     * @see ImageFormat
552     * @see PixelFormat
553     */
554    public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
555        if (!isOutputSupportedFor(klass)) {
556            throw new IllegalArgumentException("klass was not supported");
557        }
558
559        return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
560                size, DURATION_STALL);
561    }
562
563    /**
564     * Check if this {@link StreamConfigurationMap} is equal to another
565     * {@link StreamConfigurationMap}.
566     *
567     * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
568     *
569     * @return {@code true} if the objects were equal, {@code false} otherwise
570     */
571    @Override
572    public boolean equals(final Object obj) {
573        if (obj == null) {
574            return false;
575        }
576        if (this == obj) {
577            return true;
578        }
579        if (obj instanceof StreamConfigurationMap) {
580            final StreamConfigurationMap other = (StreamConfigurationMap) obj;
581            // XX: do we care about order?
582            return Arrays.equals(mConfigurations, other.mConfigurations) &&
583                    Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) &&
584                    Arrays.equals(mStallDurations, other.mStallDurations);
585        }
586        return false;
587    }
588
589    /**
590     * {@inheritDoc}
591     */
592    @Override
593    public int hashCode() {
594        // XX: do we care about order?
595        return HashCodeHelpers.hashCode(mConfigurations, mMinFrameDurations, mStallDurations);
596    }
597
598    // Check that the argument is supported by #getOutputFormats or #getInputFormats
599    private int checkArgumentFormatSupported(int format, boolean output) {
600        checkArgumentFormat(format);
601
602        int[] formats = output ? getOutputFormats() : getInputFormats();
603        for (int i = 0; i < formats.length; ++i) {
604            if (format == formats[i]) {
605                return format;
606            }
607        }
608
609        throw new IllegalArgumentException(String.format(
610                "format %x is not supported by this stream configuration map", format));
611    }
612
613    /**
614     * Ensures that the format is either user-defined or implementation defined.
615     *
616     * <p>If a format has a different internal representation than the public representation,
617     * passing in the public representation here will fail.</p>
618     *
619     * <p>For example if trying to use {@link ImageFormat#JPEG}:
620     * it has a different public representation than the internal representation
621     * {@code HAL_PIXEL_FORMAT_BLOB}, this check will fail.</p>
622     *
623     * <p>Any invalid/undefined formats will raise an exception.</p>
624     *
625     * @param format image format
626     * @return the format
627     *
628     * @throws IllegalArgumentException if the format was invalid
629     */
630    static int checkArgumentFormatInternal(int format) {
631        switch (format) {
632            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
633            case HAL_PIXEL_FORMAT_BLOB:
634                return format;
635            case ImageFormat.JPEG:
636                throw new IllegalArgumentException(
637                        "ImageFormat.JPEG is an unknown internal format");
638            default:
639                return checkArgumentFormat(format);
640        }
641    }
642
643    /**
644     * Ensures that the format is publicly user-defined in either ImageFormat or PixelFormat.
645     *
646     * <p>If a format has a different public representation than the internal representation,
647     * passing in the internal representation here will fail.</p>
648     *
649     * <p>For example if trying to use {@code HAL_PIXEL_FORMAT_BLOB}:
650     * it has a different internal representation than the public representation
651     * {@link ImageFormat#JPEG}, this check will fail.</p>
652     *
653     * <p>Any invalid/undefined formats will raise an exception, including implementation-defined.
654     * </p>
655     *
656     * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p>
657     *
658     * @param format image format
659     * @return the format
660     *
661     * @throws IllegalArgumentException if the format was not user-defined
662     */
663    static int checkArgumentFormat(int format) {
664        // TODO: remove this hack , CTS shouldn't have been using internal constants
665        if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
666            Log.w(TAG, "RAW_OPAQUE is not yet a published format; allowing it anyway");
667            return format;
668        }
669
670        if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
671            throw new IllegalArgumentException(String.format(
672                    "format 0x%x was not defined in either ImageFormat or PixelFormat", format));
673        }
674
675        return format;
676    }
677
678    /**
679     * Convert a public-visible {@code ImageFormat} into an internal format
680     * compatible with {@code graphics.h}.
681     *
682     * <p>In particular these formats are converted:
683     * <ul>
684     * <li>HAL_PIXEL_FORMAT_BLOB => ImageFormat.JPEG
685     * </ul>
686     * </p>
687     *
688     * <p>Passing in an implementation-defined format which has no public equivalent will fail;
689     * as will passing in a public format which has a different internal format equivalent.
690     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
691     *
692     * <p>All other formats are returned as-is, no further invalid check is performed.</p>
693     *
694     * <p>This function is the dual of {@link #imageFormatToInternal}.</p>
695     *
696     * @param format image format from {@link ImageFormat} or {@link PixelFormat}
697     * @return the converted image formats
698     *
699     * @throws IllegalArgumentException
700     *          if {@code format} is {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED} or
701     *          {@link ImageFormat#JPEG}
702     *
703     * @see ImageFormat
704     * @see PixelFormat
705     * @see #checkArgumentFormat
706     */
707    static int imageFormatToPublic(int format) {
708        switch (format) {
709            case HAL_PIXEL_FORMAT_BLOB:
710                return ImageFormat.JPEG;
711            case ImageFormat.JPEG:
712                throw new IllegalArgumentException(
713                        "ImageFormat.JPEG is an unknown internal format");
714            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
715                throw new IllegalArgumentException(
716                        "IMPLEMENTATION_DEFINED must not leak to public API");
717            default:
718                return format;
719        }
720    }
721
722    /**
723     * Convert image formats from internal to public formats (in-place).
724     *
725     * @param formats an array of image formats
726     * @return {@code formats}
727     *
728     * @see #imageFormatToPublic
729     */
730    static int[] imageFormatToPublic(int[] formats) {
731        if (formats == null) {
732            return null;
733        }
734
735        for (int i = 0; i < formats.length; ++i) {
736            formats[i] = imageFormatToPublic(formats[i]);
737        }
738
739        return formats;
740    }
741
742    /**
743     * Convert a public format compatible with {@code ImageFormat} to an internal format
744     * from {@code graphics.h}.
745     *
746     * <p>In particular these formats are converted:
747     * <ul>
748     * <li>ImageFormat.JPEG => HAL_PIXEL_FORMAT_BLOB
749     * </ul>
750     * </p>
751     *
752     * <p>Passing in an implementation-defined format here will fail (it's not a public format);
753     * as will passing in an internal format which has a different public format equivalent.
754     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
755     *
756     * <p>All other formats are returned as-is, no invalid check is performed.</p>
757     *
758     * <p>This function is the dual of {@link #imageFormatToPublic}.</p>
759     *
760     * @param format public image format from {@link ImageFormat} or {@link PixelFormat}
761     * @return the converted image formats
762     *
763     * @see ImageFormat
764     * @see PixelFormat
765     *
766     * @throws IllegalArgumentException
767     *              if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
768     */
769    static int imageFormatToInternal(int format) {
770        switch (format) {
771            case ImageFormat.JPEG:
772                return HAL_PIXEL_FORMAT_BLOB;
773            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
774                throw new IllegalArgumentException(
775                        "IMPLEMENTATION_DEFINED is not allowed via public API");
776            default:
777                return format;
778        }
779    }
780
781    /**
782     * Convert image formats from public to internal formats (in-place).
783     *
784     * @param formats an array of image formats
785     * @return {@code formats}
786     *
787     * @see #imageFormatToInternal
788     *
789     * @hide
790     */
791    public static int[] imageFormatToInternal(int[] formats) {
792        if (formats == null) {
793            return null;
794        }
795
796        for (int i = 0; i < formats.length; ++i) {
797            formats[i] = imageFormatToInternal(formats[i]);
798        }
799
800        return formats;
801    }
802
803    private Size[] getPublicFormatSizes(int format, boolean output) {
804        try {
805            checkArgumentFormatSupported(format, output);
806        } catch (IllegalArgumentException e) {
807            return null;
808        }
809
810        format = imageFormatToInternal(format);
811
812        return getInternalFormatSizes(format, output);
813    }
814
815    private Size[] getInternalFormatSizes(int format, boolean output) {
816        HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
817
818        Integer sizesCount = formatsMap.get(format);
819        if (sizesCount == null) {
820            throw new IllegalArgumentException("format not available");
821        }
822
823        int len = sizesCount;
824        Size[] sizes = new Size[len];
825        int sizeIndex = 0;
826
827        for (StreamConfiguration config : mConfigurations) {
828            if (config.getFormat() == format && config.isOutput() == output) {
829                sizes[sizeIndex++] = config.getSize();
830            }
831        }
832
833        if (sizeIndex != len) {
834            throw new AssertionError(
835                    "Too few sizes (expected " + len + ", actual " + sizeIndex + ")");
836        }
837
838        return sizes;
839    }
840
841    /** Get the list of publically visible output formats; does not include IMPL_DEFINED */
842    private int[] getPublicFormats(boolean output) {
843        int[] formats = new int[getPublicFormatCount(output)];
844
845        int i = 0;
846
847        for (int format : getFormatsMap(output).keySet()) {
848            if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
849                formats[i++] = format;
850            }
851        }
852
853        if (formats.length != i) {
854            throw new AssertionError("Too few formats " + i + ", expected " + formats.length);
855        }
856
857        return imageFormatToPublic(formats);
858    }
859
860    /** Get the format -> size count map for either output or input formats */
861    private HashMap<Integer, Integer> getFormatsMap(boolean output) {
862        return output ? mOutputFormats : mInputFormats;
863    }
864
865    private long getInternalFormatDuration(int format, Size size, int duration) {
866        // assume format is already checked, since its internal
867
868        if (!arrayContains(getInternalFormatSizes(format, /*output*/true), size)) {
869            throw new IllegalArgumentException("size was not supported");
870        }
871
872        StreamConfigurationDuration[] durations = getDurations(duration);
873
874        for (StreamConfigurationDuration configurationDuration : durations) {
875            if (configurationDuration.getFormat() == format &&
876                    configurationDuration.getWidth() == size.getWidth() &&
877                    configurationDuration.getHeight() == size.getHeight()) {
878                return configurationDuration.getDuration();
879            }
880        }
881
882        return getDurationDefault(duration);
883    }
884
885    /**
886     * Get the durations array for the kind of duration
887     *
888     * @see #DURATION_MIN_FRAME
889     * @see #DURATION_STALL
890     * */
891    private StreamConfigurationDuration[] getDurations(int duration) {
892        switch (duration) {
893            case DURATION_MIN_FRAME:
894                return mMinFrameDurations;
895            case DURATION_STALL:
896                return mStallDurations;
897            default:
898                throw new IllegalArgumentException("duration was invalid");
899        }
900    }
901
902    private long getDurationDefault(int duration) {
903        switch (duration) {
904            case DURATION_MIN_FRAME:
905                return NO_MIN_FRAME_DURATION;
906            case DURATION_STALL:
907                return 0L; // OK. A lack of a stall duration implies a 0 stall duration
908            default:
909                throw new IllegalArgumentException("duration was invalid");
910        }
911    }
912
913    /** Count the number of publicly-visible output formats */
914    private int getPublicFormatCount(boolean output) {
915        HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
916
917        int size = formatsMap.size();
918        if (formatsMap.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) {
919            size -= 1;
920        }
921        return size;
922    }
923
924    private static <T> boolean arrayContains(T[] array, T element) {
925        if (array == null) {
926            return false;
927        }
928
929        for (T el : array) {
930            if (Objects.equals(el, element)) {
931                return true;
932            }
933        }
934
935        return false;
936    }
937
938    // from system/core/include/system/graphics.h
939    private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
940    private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
941    private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24;
942
943    /**
944     * @see #getDurations(int)
945     * @see #getDurationDefault(int)
946     */
947    private static final int DURATION_MIN_FRAME = 0;
948    private static final int DURATION_STALL = 1;
949
950    private final StreamConfiguration[] mConfigurations;
951    private final StreamConfigurationDuration[] mMinFrameDurations;
952    private final StreamConfigurationDuration[] mStallDurations;
953
954    /** ImageFormat -> num output sizes mapping */
955    private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mOutputFormats =
956            new HashMap<Integer, Integer>();
957    /** ImageFormat -> num input sizes mapping */
958    private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mInputFormats =
959            new HashMap<Integer, Integer>();
960
961}
962