307 lines
12 KiB
Java
307 lines
12 KiB
Java
![]() |
/*
|
||
|
* Copyright 2015 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.hardware.camera2.utils;
|
||
|
|
||
|
import static android.system.OsConstants.EINVAL;
|
||
|
|
||
|
import static com.android.internal.util.Preconditions.checkNotNull;
|
||
|
|
||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||
|
import android.graphics.ImageFormat;
|
||
|
import android.graphics.PixelFormat;
|
||
|
import android.hardware.HardwareBuffer;
|
||
|
import android.hardware.camera2.params.StreamConfigurationMap;
|
||
|
import android.util.Range;
|
||
|
import android.util.Size;
|
||
|
import android.view.Surface;
|
||
|
|
||
|
import java.util.Arrays;
|
||
|
import java.util.Collection;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.List;
|
||
|
|
||
|
/**
|
||
|
* Various Surface utilities.
|
||
|
*/
|
||
|
public class SurfaceUtils {
|
||
|
|
||
|
// Usage flags not yet included in HardwareBuffer
|
||
|
private static final int USAGE_RENDERSCRIPT = 0x00100000;
|
||
|
private static final int USAGE_HW_COMPOSER = 0x00000800;
|
||
|
|
||
|
// Image formats not yet included in PixelFormat
|
||
|
private static final int BGRA_8888 = 0x5;
|
||
|
|
||
|
private static final int BAD_VALUE = -EINVAL;
|
||
|
|
||
|
/**
|
||
|
* Check if a surface is for preview consumer based on consumer end point Gralloc usage flags.
|
||
|
*
|
||
|
* @param surface The surface to be checked.
|
||
|
* @return true if the surface is for preview consumer, false otherwise.
|
||
|
*/
|
||
|
public static boolean isSurfaceForPreview(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
long usageFlags = nativeDetectSurfaceUsageFlags(surface);
|
||
|
long disallowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE | USAGE_RENDERSCRIPT
|
||
|
| HardwareBuffer.USAGE_CPU_READ_OFTEN;
|
||
|
long allowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
|
||
|
| HardwareBuffer.USAGE_GPU_COLOR_OUTPUT;
|
||
|
boolean previewConsumer = ((usageFlags & disallowedFlags) == 0
|
||
|
&& (usageFlags & allowedFlags) != 0);
|
||
|
int surfaceFormat = getSurfaceFormat(surface);
|
||
|
|
||
|
return previewConsumer;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the surface is for hardware video encoder consumer based on consumer end point
|
||
|
* Gralloc usage flags.
|
||
|
*
|
||
|
* @param surface The surface to be checked.
|
||
|
* @return true if the surface is for hardware video encoder consumer, false otherwise.
|
||
|
*/
|
||
|
public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
long usageFlags = nativeDetectSurfaceUsageFlags(surface);
|
||
|
long disallowedFlags = USAGE_HW_COMPOSER
|
||
|
| USAGE_RENDERSCRIPT | HardwareBuffer.USAGE_CPU_READ_OFTEN;
|
||
|
long allowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE;
|
||
|
boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0
|
||
|
&& (usageFlags & allowedFlags) != 0);
|
||
|
|
||
|
int surfaceFormat = getSurfaceFormat(surface);
|
||
|
|
||
|
return videoEncoderConsumer;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the native object id of a surface.
|
||
|
*
|
||
|
* @param surface The surface to be checked.
|
||
|
* @return the native object id of the surface, 0 if surface is not backed by a native object.
|
||
|
*/
|
||
|
public static long getSurfaceId(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
try {
|
||
|
return nativeGetSurfaceId(surface);
|
||
|
} catch (IllegalArgumentException e) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the surface usage bits.
|
||
|
*
|
||
|
* @param surface The surface to be queried for usage.
|
||
|
* @return the native object id of the surface, 0 if surface is not backed by a native object.
|
||
|
*/
|
||
|
public static long getSurfaceUsage(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
try {
|
||
|
return nativeDetectSurfaceUsageFlags(surface);
|
||
|
} catch (IllegalArgumentException e) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Get the Surface size.
|
||
|
*
|
||
|
* @param surface The surface to be queried for size.
|
||
|
* @return Size of the surface.
|
||
|
*
|
||
|
* @throws IllegalArgumentException if the surface is already abandoned.
|
||
|
*/
|
||
|
@UnsupportedAppUsage
|
||
|
public static Size getSurfaceSize(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
|
||
|
int[] dimens = new int[2];
|
||
|
int errorFlag = nativeDetectSurfaceDimens(surface, /*out*/dimens);
|
||
|
if (errorFlag == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
|
||
|
|
||
|
return new Size(dimens[0], dimens[1]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the Surface format.
|
||
|
*
|
||
|
* @param surface The surface to be queried for format.
|
||
|
* @return format of the surface.
|
||
|
*
|
||
|
* @throws IllegalArgumentException if the surface is already abandoned.
|
||
|
*/
|
||
|
public static int getSurfaceFormat(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
int surfaceType = nativeDetectSurfaceType(surface);
|
||
|
if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
|
||
|
|
||
|
// TODO: remove this override since the default format should be
|
||
|
// ImageFormat.PRIVATE. b/9487482
|
||
|
if ((surfaceType >= PixelFormat.RGBA_8888
|
||
|
&& surfaceType <= BGRA_8888)) {
|
||
|
surfaceType = ImageFormat.PRIVATE;
|
||
|
}
|
||
|
return surfaceType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Detect and retrieve the Surface format without any
|
||
|
* additional overrides.
|
||
|
*
|
||
|
* @param surface The surface to be queried for format.
|
||
|
* @return format of the surface.
|
||
|
*
|
||
|
* @throws IllegalArgumentException if the surface is already abandoned.
|
||
|
*/
|
||
|
public static int detectSurfaceFormat(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
int surfaceType = nativeDetectSurfaceType(surface);
|
||
|
if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
|
||
|
|
||
|
return surfaceType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the Surface dataspace.
|
||
|
*
|
||
|
* @param surface The surface to be queried for dataspace.
|
||
|
* @return dataspace of the surface.
|
||
|
*
|
||
|
* @throws IllegalArgumentException if the surface is already abandoned.
|
||
|
*/
|
||
|
public static int getSurfaceDataspace(Surface surface) {
|
||
|
checkNotNull(surface);
|
||
|
int dataSpace = nativeDetectSurfaceDataspace(surface);
|
||
|
if (dataSpace == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
|
||
|
return dataSpace;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return true is the consumer is one of the consumers that can accept
|
||
|
* producer overrides of the default dimensions and format.
|
||
|
*
|
||
|
*/
|
||
|
public static boolean isFlexibleConsumer(Surface output) {
|
||
|
checkNotNull(output);
|
||
|
long usageFlags = nativeDetectSurfaceUsageFlags(output);
|
||
|
|
||
|
// Keep up to date with allowed consumer types in
|
||
|
// frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
|
||
|
long disallowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE | USAGE_RENDERSCRIPT;
|
||
|
long allowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
|
||
|
| HardwareBuffer.USAGE_CPU_READ_OFTEN
|
||
|
| USAGE_HW_COMPOSER;
|
||
|
boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0
|
||
|
&& (usageFlags & allowedFlags) != 0);
|
||
|
return flexibleConsumer;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A high speed output surface can only be preview or hardware encoder surface.
|
||
|
*
|
||
|
* @param surface The high speed output surface to be checked.
|
||
|
*/
|
||
|
private static void checkHighSpeedSurfaceFormat(Surface surface) {
|
||
|
int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
|
||
|
|
||
|
if (surfaceFormat != ImageFormat.PRIVATE) {
|
||
|
throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not"
|
||
|
+ " for preview or hardware video encoding!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Verify that that the surfaces are valid for high-speed recording mode,
|
||
|
* and that the FPS range is supported
|
||
|
*
|
||
|
* @param surfaces the surfaces to verify as valid in terms of size and format
|
||
|
* @param fpsRange the target high-speed FPS range to validate
|
||
|
* @param config The stream configuration map for the device in question
|
||
|
*/
|
||
|
public static void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces,
|
||
|
Range<Integer> fpsRange, StreamConfigurationMap config) {
|
||
|
if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) {
|
||
|
throw new IllegalArgumentException("Output target surface list must not be null and"
|
||
|
+ " the size must be 1 or 2");
|
||
|
}
|
||
|
|
||
|
List<Size> highSpeedSizes = null;
|
||
|
if (fpsRange == null) {
|
||
|
highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
|
||
|
} else {
|
||
|
// Check the FPS range first if provided
|
||
|
Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges();
|
||
|
if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) {
|
||
|
throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the"
|
||
|
+ " request is not a supported high speed fps range " +
|
||
|
Arrays.toString(highSpeedFpsRanges));
|
||
|
}
|
||
|
highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange));
|
||
|
}
|
||
|
|
||
|
for (Surface surface : surfaces) {
|
||
|
checkHighSpeedSurfaceFormat(surface);
|
||
|
|
||
|
// Surface size must be supported high speed sizes.
|
||
|
Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
|
||
|
if (!highSpeedSizes.contains(surfaceSize)) {
|
||
|
throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is"
|
||
|
+ " not part of the high speed supported size list " +
|
||
|
Arrays.toString(highSpeedSizes.toArray()));
|
||
|
}
|
||
|
// Each output surface must be either preview surface or recording surface.
|
||
|
if (!SurfaceUtils.isSurfaceForPreview(surface) &&
|
||
|
!SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
|
||
|
throw new IllegalArgumentException("This output surface is neither preview nor "
|
||
|
+ "hardware video encoding surface");
|
||
|
}
|
||
|
if (SurfaceUtils.isSurfaceForPreview(surface) &&
|
||
|
SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
|
||
|
throw new IllegalArgumentException("This output surface can not be both preview"
|
||
|
+ " and hardware video encoding surface");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For 2 output surface case, they shouldn't be same type.
|
||
|
if (surfaces.size() == 2) {
|
||
|
// Up to here, each surface can only be either preview or recording.
|
||
|
Iterator<Surface> iterator = surfaces.iterator();
|
||
|
boolean isFirstSurfacePreview =
|
||
|
SurfaceUtils.isSurfaceForPreview(iterator.next());
|
||
|
boolean isSecondSurfacePreview =
|
||
|
SurfaceUtils.isSurfaceForPreview(iterator.next());
|
||
|
if (isFirstSurfacePreview == isSecondSurfacePreview) {
|
||
|
throw new IllegalArgumentException("The 2 output surfaces must have different"
|
||
|
+ " type");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static native int nativeDetectSurfaceType(Surface surface);
|
||
|
|
||
|
private static native int nativeDetectSurfaceDataspace(Surface surface);
|
||
|
|
||
|
private static native long nativeDetectSurfaceUsageFlags(Surface surface);
|
||
|
|
||
|
private static native int nativeDetectSurfaceDimens(Surface surface,
|
||
|
/*out*/int[/*2*/] dimens);
|
||
|
|
||
|
private static native long nativeGetSurfaceId(Surface surface);
|
||
|
}
|