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