diff options
author | Zhijun He <zhijunhe@google.com> | 2015-06-01 16:36:06 -0700 |
---|---|---|
committer | Zhijun He <zhijunhe@google.com> | 2015-06-04 15:22:00 -0700 |
commit | a7677722304670dc07feef242156b97e6bb51bcd (patch) | |
tree | 2745720687e323dbd4a248f2368e2e42a6d339ec /core/java | |
parent | f1bbdf5182107aa7779cd1a91d8652f04fa1b399 (diff) | |
download | frameworks_base-a7677722304670dc07feef242156b97e6bb51bcd.zip frameworks_base-a7677722304670dc07feef242156b97e6bb51bcd.tar.gz frameworks_base-a7677722304670dc07feef242156b97e6bb51bcd.tar.bz2 |
Camera2: implement high speed video APIs
Bug: 21442271
Change-Id: Ia0ae5bbd3e8c81bad293c29987301a2457817d12
Diffstat (limited to 'core/java')
7 files changed, 375 insertions, 19 deletions
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index b2c1c71..bc625dd 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -169,6 +169,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final HashSet<Surface> mSurfaceSet; private final CameraMetadataNative mSettings; private boolean mIsReprocess; + // If this request is part of constrained high speed request list that was created by + // {@link CameraDevice#createConstrainedHighSpeedRequestList}. + private boolean mIsPartOfCHSRequestList = false; // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). private int mReprocessableSessionId; @@ -197,6 +200,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; + mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList; mReprocessableSessionId = source.mReprocessableSessionId; mUserTag = source.mUserTag; } @@ -321,6 +325,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** + * <p>Determine if this request is part of a constrained high speed request list that was + * created by {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high + * speed request list contains some constrained high speed capture requests with certain + * interleaved pattern that is suitable for high speed preview/video streaming. An active + * constrained high speed capture session only accepts constrained high speed request lists. + * This method can be used to do the sanity check when a constrained high speed capture session + * receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or + * {@link CameraCaptureSession#captureBurst}. + * </p> + * + * + * @return {@code true} if this request is part of a constrained high speed request list, + * {@code false} otherwise. + * + * @hide + */ + public boolean isPartOfCRequestList() { + return mIsPartOfCHSRequestList; + } + + /** + * Returns a copy of the underlying {@link CameraMetadataNative}. + * @hide + */ + public CameraMetadataNative getNativeCopy() { + return new CameraMetadataNative(mSettings); + } + + /** * Get the reprocessable session ID this reprocess capture request is associated with. * * @return the reprocessable session ID this reprocess capture request is associated with @@ -547,6 +580,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** + * <p>Mark this request as part of a constrained high speed request list created by + * {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high speed + * request list contains some constrained high speed capture requests with certain + * interleaved pattern that is suitable for high speed preview/video streaming.</p> + * + * @hide + */ + public void setPartOfCHSRequestList(boolean partOfCHSList) { + mRequest.mIsPartOfCHSRequestList = partOfCHSList; + } + + /** * Build a request using the current target Surfaces and settings. * <p>Note that, although it is possible to create a {@code CaptureRequest} with no target * {@link Surface}s, passing such a request into {@link CameraCaptureSession#capture}, @@ -563,7 +608,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return new CaptureRequest(mRequest); } - /** * @hide */ diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl index 375b310..1574f93 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl @@ -61,7 +61,7 @@ interface ICameraDeviceUser * must be called before any requests can be submitted. * <p> */ - int endConfigure(); + int endConfigure(boolean isConstrainedHighSpeed); int deleteStream(int streamId); diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index ab0f607..cbc85f3 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -60,6 +60,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl; /** Internal handler; used for all incoming events to preserve total order */ private final Handler mDeviceHandler; + private final boolean mIsConstrainedHighSpeedSession; /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */ private final TaskDrainer<Integer> mSequenceDrainer; @@ -88,13 +89,14 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, - Handler deviceStateHandler, boolean configureSuccess) { + Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) { if (outputs == null || outputs.isEmpty()) { throw new IllegalArgumentException("outputs must be a non-null, non-empty list"); } else if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } + mIsConstrainedHighSpeedSession = isConstrainedHighSpeed; mId = id; mIdString = String.format("Session %d: ", mId); @@ -134,6 +136,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } } + + private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) { + checkCollectionNotEmpty(requestList, "High speed request list"); + for (CaptureRequest request : requestList) { + if (!request.isPartOfCRequestList()) { + return false; + } + } + return true; + } + + /** + * If the session is constrained high speed session, it only accept constrained high speed + * request list. + */ + private void checkConstrainedHighSpeedRequestSanity(List<CaptureRequest> requestList) { + if (mIsConstrainedHighSpeedSession) { + if (!isConstrainedHighSpeedRequestList(requestList)) { + throw new IllegalArgumentException("It is only allowed to submit a constrained " + + "high speed request list to a constrianed high speed session!!!"); + } + } + } + @Override public CameraDevice getDevice() { return mDeviceImpl; @@ -155,6 +181,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } + if (mIsConstrainedHighSpeedSession) { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } checkNotClosed(); @@ -178,6 +208,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { throw new IllegalArgumentException("Requests must have at least one element"); } + checkConstrainedHighSpeedRequestSanity(requests); + for (CaptureRequest request : requests) { if (request.isReprocess()) { if (!isReprocessable()) { @@ -212,7 +244,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } else if (request.isReprocess()) { throw new IllegalArgumentException("repeating reprocess requests are not supported"); } - + if (mIsConstrainedHighSpeedSession) { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } checkNotClosed(); @@ -236,6 +271,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { throw new IllegalArgumentException("requests must have at least one element"); } + checkConstrainedHighSpeedRequestSanity(requests); + for (CaptureRequest r : requests) { if (r.isReprocess()) { throw new IllegalArgumentException("repeating reprocess burst requests are not " + @@ -704,7 +741,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { // everything is idle. try { // begin transition to unconfigured - mDeviceImpl.configureStreamsChecked(null, null); + mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null, + /*isConstrainedHighSpeed*/false); } catch (CameraAccessException e) { // OK: do not throw checked exceptions. Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 16701e5..e975105 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -18,6 +18,7 @@ package android.hardware.camera2.impl; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; +import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -35,17 +36,22 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; import android.hardware.camera2.utils.LongParcelable; +import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import android.util.Range; import android.util.Size; import android.util.SparseArray; import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -326,7 +332,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface s : outputs) { outputConfigs.add(new OutputConfiguration(s)); } - configureStreamsChecked(/*inputConfig*/null, outputConfigs); + configureStreamsChecked(/*inputConfig*/null, outputConfigs, + /*isConstrainedHighSpeed*/false); } @@ -344,12 +351,14 @@ public class CameraDeviceImpl extends CameraDevice { * * @param inputConfig input configuration or {@code null} for no input * @param outputs a list of one or more surfaces, or {@code null} to unconfigure + * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output. * @return whether or not the configuration was successful * * @throws CameraAccessException if there were any unexpected problems during configuration */ public boolean configureStreamsChecked(InputConfiguration inputConfig, - List<OutputConfiguration> outputs) throws CameraAccessException { + List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed) + throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { outputs = new ArrayList<OutputConfiguration>(); @@ -422,7 +431,7 @@ public class CameraDeviceImpl extends CameraDevice { } try { - mRemoteDevice.endConfigure(); + mRemoteDevice.endConfigure(isConstrainedHighSpeed); } catch (IllegalArgumentException e) { // OK. camera service can reject stream config if it's not supported by HAL @@ -463,7 +472,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(null, outConfigurations, callback, handler); + createCaptureSessionInternal(null, outConfigurations, callback, handler, + /*isConstrainedHighSpeed*/false); } @Override @@ -475,7 +485,8 @@ public class CameraDeviceImpl extends CameraDevice { Log.d(TAG, "createCaptureSessionByOutputConfiguration"); } - createCaptureSessionInternal(null, outputConfigurations, callback, handler); + createCaptureSessionInternal(null, outputConfigurations, callback, handler, + /*isConstrainedHighSpeed*/false); } @Override @@ -494,13 +505,14 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler); + createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, + /*isConstrainedHighSpeed*/false); } private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, - CameraCaptureSession.StateCallback callback, Handler handler) - throws CameraAccessException { + CameraCaptureSession.StateCallback callback, Handler handler, + boolean isConstrainedHighSpeed) throws CameraAccessException { synchronized(mInterfaceLock) { if (DEBUG) { Log.d(TAG, "createCaptureSessionInternal"); @@ -508,6 +520,11 @@ public class CameraDeviceImpl extends CameraDevice { checkIfCameraClosedOrInError(); + if (isConstrainedHighSpeed && inputConfig != null) { + throw new IllegalArgumentException("Constrained high speed session doesn't support" + + " input configuration yet."); + } + // Notify current session that it's going away, before starting camera operations // After this call completes, the session is not allowed to call into CameraDeviceImpl if (mCurrentSession != null) { @@ -520,7 +537,8 @@ public class CameraDeviceImpl extends CameraDevice { Surface input = null; try { // configure streams and then block until IDLE - configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations); + configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, + isConstrainedHighSpeed); if (inputConfig != null) { input = new Surface(); mRemoteDevice.getInputSurface(/*out*/input); @@ -545,7 +563,7 @@ public class CameraDeviceImpl extends CameraDevice { CameraCaptureSessionImpl newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, outSurfaces, callback, handler, this, mDeviceHandler, - configureSuccess); + configureSuccess, isConstrainedHighSpeed); // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; @@ -1907,17 +1925,156 @@ public class CameraDeviceImpl extends CameraDevice { return mCharacteristics; } + private void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces, + Range<Integer> fpsRange) { + 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"); + } + + StreamConfigurationMap config = + getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + 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) { + // Surface size must be supported high speed sizes. + Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); + int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); + + if (surfaceFormat != ImageFormat.PRIVATE) { + throw new IllegalArgumentException("Surface format is not for preview or" + + " hardware video encoding" + surfaceFormat); + } + + 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"); + } + } + } + @Override public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { - // TODO: to be implemented - throw new UnsupportedOperationException("To be implemented!!!!"); + if (outputs == null || outputs.size() == 0 || outputs.size() > 2) { + throw new IllegalArgumentException( + "Output surface list must not be null and the size must be no more than 2"); + } + checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null); + + List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); + for (Surface surface : outputs) { + outConfigurations.add(new OutputConfiguration(surface)); + } + createCaptureSessionInternal(null, outConfigurations, callback, handler, + /*isConstrainedHighSpeed*/true); } @Override public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request) throws CameraAccessException { - throw new UnsupportedOperationException("To be implemented!!!!"); + if (request == null) { + throw new IllegalArgumentException("Input capture request must not be null"); + } + Collection<Surface> outputSurfaces = request.getTargets(); + Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange); + + // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize + // the preview frame rate, should use maxBatch size for that high speed stream + // configuration. We choose the former for now. + int requestListSize = fpsRange.getUpper() / 30; + List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); + + // Prepare the Request builders: need carry over the request controls. + // First, create a request builder that will only include preview or recording target. + CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy()); + CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder( + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + + // Overwrite the capture intent to make sure a good value is set. + Surface[] surfaces = (Surface[])outputSurfaces.toArray(); + if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(surfaces[0])) { + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); + } else { + // Video only, or preview + video + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + } + singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + + // Second, Create a request builder that will include both preview and recording targets. + CaptureRequest.Builder doubleTargetRequestBuilder = null; + if (outputSurfaces.size() == 2) { + doubleTargetRequestBuilder = new CaptureRequest.Builder( + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + doubleTargetRequestBuilder.addTarget(surfaces[0]); + doubleTargetRequestBuilder.addTarget(surfaces[1]); + doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + // Make sure singleTargetRequestBuilder contains only recording surface for + // preview + recording case. + Surface recordingSurface = surfaces[0]; + if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { + recordingSurface = surfaces[1]; + } + singleTargetRequestBuilder.addTarget(recordingSurface); + } else { + // Single output case: either recording or preview. + singleTargetRequestBuilder.addTarget(surfaces[0]); + } + + // Generate the final request list. + for (int i = 0; i < requestListSize; i++) { + if (i == 0 && doubleTargetRequestBuilder != null) { + // First request should be recording + preview request + requestList.add(doubleTargetRequestBuilder.build()); + } else { + requestList.add(singleTargetRequestBuilder.build()); + } + } + + return Collections.unmodifiableList(requestList); } } diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index bc0a3a8..e963a0d 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -465,7 +465,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override - public int endConfigure() { + public int endConfigure(boolean isConstrainedHighSpeed) { if (DEBUG) { Log.d(TAG, "endConfigure called."); } diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index 098c2d8..cc9d496 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -82,6 +82,7 @@ public class LegacyCameraDevice implements AutoCloseable { private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003; private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100; private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800; + private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200; private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000; public static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding @@ -549,6 +550,42 @@ public class LegacyCameraDevice implements AutoCloseable { return flexibleConsumer; } + public static boolean isPreviewConsumer(Surface output) { + int usageFlags = detectSurfaceUsageFlags(output); + int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT | + GRALLOC_USAGE_SW_READ_OFTEN; + int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | + GRALLOC_USAGE_HW_RENDER; + boolean previewConsumer = ((usageFlags & disallowedFlags) == 0 && + (usageFlags & allowedFlags) != 0); + int surfaceFormat = ImageFormat.UNKNOWN; + try { + surfaceFormat = detectSurfaceType(output); + } catch(BufferQueueAbandonedException e) { + throw new IllegalArgumentException("Surface was abandoned", e); + } + + return previewConsumer && (surfaceFormat == ImageFormat.PRIVATE); + } + + public static boolean isVideoEncoderConsumer(Surface output) { + int usageFlags = detectSurfaceUsageFlags(output); + int disallowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_COMPOSER | + GRALLOC_USAGE_RENDERSCRIPT | GRALLOC_USAGE_SW_READ_OFTEN; + int allowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER; + boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0 && + (usageFlags & allowedFlags) != 0); + + int surfaceFormat = ImageFormat.UNKNOWN; + try { + surfaceFormat = detectSurfaceType(output); + } catch(BufferQueueAbandonedException e) { + throw new IllegalArgumentException("Surface was abandoned", e); + } + + return videoEncoderConsumer && (surfaceFormat == ImageFormat.PRIVATE); + } + /** * Query the surface for its currently configured usage flags */ diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java new file mode 100644 index 0000000..79d82a8 --- /dev/null +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -0,0 +1,80 @@ +/* + * 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 android.hardware.camera2.legacy.LegacyCameraDevice; +import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException; +import android.util.Size; +import android.view.Surface; + +/** + * Various Surface utilities. + */ +public class SurfaceUtils { + + /** + * Check if a surface is for preview consumer. + * + * @param surface The surface to be checked. + * @return true if the surface is for preview consumer, false otherwise. + */ + public static boolean isSurfaceForPreview(Surface surface) { + return LegacyCameraDevice.isPreviewConsumer(surface); + } + + /** + * Check if the surface is for hardware video encoder consumer. + * + * @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) { + return LegacyCameraDevice.isVideoEncoderConsumer(surface); + } + + /** + * Get the Surface size. + * + * @param surface The surface to be queried for size. + * @return Size of the surface. + * + * @throw IllegalArgumentException if the surface is already abandoned. + */ + public static Size getSurfaceSize(Surface surface) { + try { + return LegacyCameraDevice.getSurfaceSize(surface); + } catch (BufferQueueAbandonedException e) { + throw new IllegalArgumentException("Surface was abandoned", e); + } + } + + /** + * Get the Surface format. + * + * @param surface The surface to be queried for format. + * @return format of the surface. + * + * @throw IllegalArgumentException if the surface is already abandoned. + */ + public static int getSurfaceFormat(Surface surface) { + try { + return LegacyCameraDevice.detectSurfaceType(surface); + } catch (BufferQueueAbandonedException e) { + throw new IllegalArgumentException("Surface was abandoned", e); + } + } +} |