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 
17 package android.hardware.camera2.utils;
18 
19 import android.graphics.ImageFormat;
20 import android.hardware.camera2.legacy.LegacyCameraDevice;
21 import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
22 import android.hardware.camera2.params.StreamConfigurationMap;
23 import android.util.Range;
24 import android.util.Size;
25 import android.view.Surface;
26 
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Iterator;
30 import java.util.List;
31 
32 /**
33  * Various Surface utilities.
34  */
35 public 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      */
isSurfaceForPreview(Surface surface)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      */
isSurfaceForHwVideoEncoder(Surface surface)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      */
getSurfaceSize(Surface surface)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      */
getSurfaceFormat(Surface surface)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      */
getSurfaceDataspace(Surface surface)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      */
isFlexibleConsumer(Surface output)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      */
checkHighSpeedSurfaceFormat(Surface surface)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      */
checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces, Range<Integer> fpsRange, StreamConfigurationMap config)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