SurfaceUtils.java revision 639fffee624302ec5b175503d7bd8a441340a629
1/*
2 * Copyright 2015 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.utils;
18
19import android.graphics.ImageFormat;
20import android.hardware.camera2.legacy.LegacyCameraDevice;
21import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
22import android.hardware.camera2.params.StreamConfigurationMap;
23import android.util.Range;
24import android.util.Size;
25import android.view.Surface;
26
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.Iterator;
30import java.util.List;
31
32/**
33 * Various Surface utilities.
34 */
35public class SurfaceUtils {
36
37    /**
38     * Check if a surface is for preview consumer based on consumer end point Gralloc usage flags.
39     *
40     * @param surface The surface to be checked.
41     * @return true if the surface is for preview consumer, false otherwise.
42     */
43    public static boolean isSurfaceForPreview(Surface surface) {
44        return LegacyCameraDevice.isPreviewConsumer(surface);
45    }
46
47    /**
48     * Check if the surface is for hardware video encoder consumer based on consumer end point
49     * Gralloc usage flags.
50     *
51     * @param surface The surface to be checked.
52     * @return true if the surface is for hardware video encoder consumer, false otherwise.
53     */
54    public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
55        return LegacyCameraDevice.isVideoEncoderConsumer(surface);
56    }
57
58    /**
59     * Get the Surface size.
60     *
61     * @param surface The surface to be queried for size.
62     * @return Size of the surface.
63     *
64     * @throws IllegalArgumentException if the surface is already abandoned.
65     */
66    public static Size getSurfaceSize(Surface surface) {
67        try {
68            return LegacyCameraDevice.getSurfaceSize(surface);
69        } catch (BufferQueueAbandonedException e) {
70            throw new IllegalArgumentException("Surface was abandoned", e);
71        }
72    }
73
74    /**
75     * Get the Surface format.
76     *
77     * @param surface The surface to be queried for format.
78     * @return format of the surface.
79     *
80     * @throws IllegalArgumentException if the surface is already abandoned.
81     */
82    public static int getSurfaceFormat(Surface surface) {
83        try {
84            return LegacyCameraDevice.detectSurfaceType(surface);
85        } catch (BufferQueueAbandonedException e) {
86            throw new IllegalArgumentException("Surface was abandoned", e);
87        }
88    }
89
90    /**
91     * Get the Surface dataspace.
92     *
93     * @param surface The surface to be queried for dataspace.
94     * @return dataspace of the surface.
95     *
96     * @throws IllegalArgumentException if the surface is already abandoned.
97     */
98    public static int getSurfaceDataspace(Surface surface) {
99        try {
100            return LegacyCameraDevice.detectSurfaceDataspace(surface);
101        } catch (BufferQueueAbandonedException e) {
102            throw new IllegalArgumentException("Surface was abandoned", e);
103        }
104    }
105
106    /**
107     * Return true is the consumer is one of the consumers that can accept
108     * producer overrides of the default dimensions and format.
109     *
110     */
111    public static boolean isFlexibleConsumer(Surface output) {
112        return LegacyCameraDevice.isFlexibleConsumer(output);
113    }
114
115    /**
116     * A high speed output surface can only be preview or hardware encoder surface.
117     *
118     * @param surface The high speed output surface to be checked.
119     */
120    private static void checkHighSpeedSurfaceFormat(Surface surface) {
121        // TODO: remove this override since the default format should be
122        // ImageFormat.PRIVATE. b/9487482
123        final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h
124        final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h
125        int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
126        if (surfaceFormat >= HAL_FORMAT_RGB_START &&
127                surfaceFormat <= HAL_FORMAT_RGB_END) {
128            surfaceFormat = ImageFormat.PRIVATE;
129        }
130
131        if (surfaceFormat != ImageFormat.PRIVATE) {
132            throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not"
133                    + " for preview or hardware video encoding!");
134        }
135    }
136
137    /**
138     * Verify that that the surfaces are valid for high-speed recording mode,
139     * and that the FPS range is supported
140     *
141     * @param surfaces the surfaces to verify as valid in terms of size and format
142     * @param fpsRange the target high-speed FPS range to validate
143     * @param config The stream configuration map for the device in question
144     */
145    public static void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces,
146            Range<Integer> fpsRange, StreamConfigurationMap config) {
147        if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) {
148            throw new IllegalArgumentException("Output target surface list must not be null and"
149                    + " the size must be 1 or 2");
150        }
151
152        List<Size> highSpeedSizes = null;
153        if (fpsRange == null) {
154            highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
155        } else {
156            // Check the FPS range first if provided
157            Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges();
158            if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) {
159                throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the"
160                        + " request is not a supported high speed fps range " +
161                        Arrays.toString(highSpeedFpsRanges));
162            }
163            highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange));
164        }
165
166        for (Surface surface : surfaces) {
167            checkHighSpeedSurfaceFormat(surface);
168
169            // Surface size must be supported high speed sizes.
170            Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
171            if (!highSpeedSizes.contains(surfaceSize)) {
172                throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is"
173                        + " not part of the high speed supported size list " +
174                        Arrays.toString(highSpeedSizes.toArray()));
175            }
176            // Each output surface must be either preview surface or recording surface.
177            if (!SurfaceUtils.isSurfaceForPreview(surface) &&
178                    !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
179                throw new IllegalArgumentException("This output surface is neither preview nor "
180                        + "hardware video encoding surface");
181            }
182            if (SurfaceUtils.isSurfaceForPreview(surface) &&
183                    SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
184                throw new IllegalArgumentException("This output surface can not be both preview"
185                        + " and hardware video encoding surface");
186            }
187        }
188
189        // For 2 output surface case, they shouldn't be same type.
190        if (surfaces.size() == 2) {
191            // Up to here, each surface can only be either preview or recording.
192            Iterator<Surface> iterator = surfaces.iterator();
193            boolean isFirstSurfacePreview =
194                    SurfaceUtils.isSurfaceForPreview(iterator.next());
195            boolean isSecondSurfacePreview =
196                    SurfaceUtils.isSurfaceForPreview(iterator.next());
197            if (isFirstSurfacePreview == isSecondSurfacePreview) {
198                throw new IllegalArgumentException("The 2 output surfaces must have different"
199                        + " type");
200            }
201        }
202    }
203
204}
205