Merge "Camera2: implement high speed video APIs" into mnc-dev
This commit is contained in:
@@ -169,6 +169,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
|
|||||||
private final HashSet<Surface> mSurfaceSet;
|
private final HashSet<Surface> mSurfaceSet;
|
||||||
private final CameraMetadataNative mSettings;
|
private final CameraMetadataNative mSettings;
|
||||||
private boolean mIsReprocess;
|
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.
|
// Each reprocess request must be tied to a reprocessable session ID.
|
||||||
// Valid only for reprocess requests (mIsReprocess == true).
|
// Valid only for reprocess requests (mIsReprocess == true).
|
||||||
private int mReprocessableSessionId;
|
private int mReprocessableSessionId;
|
||||||
@@ -197,6 +200,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
|
|||||||
mSettings = new CameraMetadataNative(source.mSettings);
|
mSettings = new CameraMetadataNative(source.mSettings);
|
||||||
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
|
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
|
||||||
mIsReprocess = source.mIsReprocess;
|
mIsReprocess = source.mIsReprocess;
|
||||||
|
mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
|
||||||
mReprocessableSessionId = source.mReprocessableSessionId;
|
mReprocessableSessionId = source.mReprocessableSessionId;
|
||||||
mUserTag = source.mUserTag;
|
mUserTag = source.mUserTag;
|
||||||
}
|
}
|
||||||
@@ -320,6 +324,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
|
|||||||
return mIsReprocess;
|
return mIsReprocess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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.
|
* Get the reprocessable session ID this reprocess capture request is associated with.
|
||||||
*
|
*
|
||||||
@@ -546,6 +579,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
|
|||||||
mRequest.mUserTag = tag;
|
mRequest.mUserTag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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.
|
* 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
|
* <p>Note that, although it is possible to create a {@code CaptureRequest} with no target
|
||||||
@@ -563,7 +608,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
|
|||||||
return new CaptureRequest(mRequest);
|
return new CaptureRequest(mRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ interface ICameraDeviceUser
|
|||||||
* must be called before any requests can be submitted.
|
* must be called before any requests can be submitted.
|
||||||
* <p>
|
* <p>
|
||||||
*/
|
*/
|
||||||
int endConfigure();
|
int endConfigure(boolean isConstrainedHighSpeed);
|
||||||
|
|
||||||
int deleteStream(int streamId);
|
int deleteStream(int streamId);
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
|
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
|
||||||
/** Internal handler; used for all incoming events to preserve total order */
|
/** Internal handler; used for all incoming events to preserve total order */
|
||||||
private final Handler mDeviceHandler;
|
private final Handler mDeviceHandler;
|
||||||
|
private final boolean mIsConstrainedHighSpeedSession;
|
||||||
|
|
||||||
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
|
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
|
||||||
private final TaskDrainer<Integer> mSequenceDrainer;
|
private final TaskDrainer<Integer> mSequenceDrainer;
|
||||||
@@ -88,13 +89,14 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
|
CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
|
||||||
CameraCaptureSession.StateCallback callback, Handler stateHandler,
|
CameraCaptureSession.StateCallback callback, Handler stateHandler,
|
||||||
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
|
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
|
||||||
Handler deviceStateHandler, boolean configureSuccess) {
|
Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) {
|
||||||
if (outputs == null || outputs.isEmpty()) {
|
if (outputs == null || outputs.isEmpty()) {
|
||||||
throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
|
throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
|
||||||
} else if (callback == null) {
|
} else if (callback == null) {
|
||||||
throw new IllegalArgumentException("callback must not be null");
|
throw new IllegalArgumentException("callback must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mIsConstrainedHighSpeedSession = isConstrainedHighSpeed;
|
||||||
mId = id;
|
mId = id;
|
||||||
mIdString = String.format("Session %d: ", mId);
|
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
|
@Override
|
||||||
public CameraDevice getDevice() {
|
public CameraDevice getDevice() {
|
||||||
return mDeviceImpl;
|
return mDeviceImpl;
|
||||||
@@ -155,6 +181,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
} else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
|
} else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
|
||||||
throw new IllegalArgumentException("capture request was created for another session");
|
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();
|
checkNotClosed();
|
||||||
|
|
||||||
@@ -178,6 +208,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
throw new IllegalArgumentException("Requests must have at least one element");
|
throw new IllegalArgumentException("Requests must have at least one element");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkConstrainedHighSpeedRequestSanity(requests);
|
||||||
|
|
||||||
for (CaptureRequest request : requests) {
|
for (CaptureRequest request : requests) {
|
||||||
if (request.isReprocess()) {
|
if (request.isReprocess()) {
|
||||||
if (!isReprocessable()) {
|
if (!isReprocessable()) {
|
||||||
@@ -212,7 +244,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
} else if (request.isReprocess()) {
|
} else if (request.isReprocess()) {
|
||||||
throw new IllegalArgumentException("repeating reprocess requests are not supported");
|
throw new IllegalArgumentException("repeating reprocess requests are not supported");
|
||||||
}
|
}
|
||||||
|
if (mIsConstrainedHighSpeedSession) {
|
||||||
|
throw new UnsupportedOperationException("Constrained high speed session doesn't support"
|
||||||
|
+ " this method");
|
||||||
|
}
|
||||||
|
|
||||||
checkNotClosed();
|
checkNotClosed();
|
||||||
|
|
||||||
@@ -236,6 +271,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
throw new IllegalArgumentException("requests must have at least one element");
|
throw new IllegalArgumentException("requests must have at least one element");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkConstrainedHighSpeedRequestSanity(requests);
|
||||||
|
|
||||||
for (CaptureRequest r : requests) {
|
for (CaptureRequest r : requests) {
|
||||||
if (r.isReprocess()) {
|
if (r.isReprocess()) {
|
||||||
throw new IllegalArgumentException("repeating reprocess burst requests are not " +
|
throw new IllegalArgumentException("repeating reprocess burst requests are not " +
|
||||||
@@ -704,7 +741,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
|
|||||||
// everything is idle.
|
// everything is idle.
|
||||||
try {
|
try {
|
||||||
// begin transition to unconfigured
|
// begin transition to unconfigured
|
||||||
mDeviceImpl.configureStreamsChecked(null, null);
|
mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
|
||||||
|
/*isConstrainedHighSpeed*/false);
|
||||||
} catch (CameraAccessException e) {
|
} catch (CameraAccessException e) {
|
||||||
// OK: do not throw checked exceptions.
|
// OK: do not throw checked exceptions.
|
||||||
Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
|
Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.hardware.camera2.impl;
|
|||||||
|
|
||||||
import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
|
import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
|
||||||
|
|
||||||
|
import android.graphics.ImageFormat;
|
||||||
import android.hardware.camera2.CameraAccessException;
|
import android.hardware.camera2.CameraAccessException;
|
||||||
import android.hardware.camera2.CameraCaptureSession;
|
import android.hardware.camera2.CameraCaptureSession;
|
||||||
import android.hardware.camera2.CameraCharacteristics;
|
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.CameraBinderDecorator;
|
||||||
import android.hardware.camera2.utils.CameraRuntimeException;
|
import android.hardware.camera2.utils.CameraRuntimeException;
|
||||||
import android.hardware.camera2.utils.LongParcelable;
|
import android.hardware.camera2.utils.LongParcelable;
|
||||||
|
import android.hardware.camera2.utils.SurfaceUtils;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Range;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -326,7 +332,8 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
for (Surface s : outputs) {
|
for (Surface s : outputs) {
|
||||||
outputConfigs.add(new OutputConfiguration(s));
|
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 inputConfig input configuration or {@code null} for no input
|
||||||
* @param outputs a list of one or more surfaces, or {@code null} to unconfigure
|
* @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
|
* @return whether or not the configuration was successful
|
||||||
*
|
*
|
||||||
* @throws CameraAccessException if there were any unexpected problems during configuration
|
* @throws CameraAccessException if there were any unexpected problems during configuration
|
||||||
*/
|
*/
|
||||||
public boolean configureStreamsChecked(InputConfiguration inputConfig,
|
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
|
// Treat a null input the same an empty list
|
||||||
if (outputs == null) {
|
if (outputs == null) {
|
||||||
outputs = new ArrayList<OutputConfiguration>();
|
outputs = new ArrayList<OutputConfiguration>();
|
||||||
@@ -422,7 +431,7 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mRemoteDevice.endConfigure();
|
mRemoteDevice.endConfigure(isConstrainedHighSpeed);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
// OK. camera service can reject stream config if it's not supported by HAL
|
// 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) {
|
for (Surface surface : outputs) {
|
||||||
outConfigurations.add(new OutputConfiguration(surface));
|
outConfigurations.add(new OutputConfiguration(surface));
|
||||||
}
|
}
|
||||||
createCaptureSessionInternal(null, outConfigurations, callback, handler);
|
createCaptureSessionInternal(null, outConfigurations, callback, handler,
|
||||||
|
/*isConstrainedHighSpeed*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -475,7 +485,8 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
Log.d(TAG, "createCaptureSessionByOutputConfiguration");
|
Log.d(TAG, "createCaptureSessionByOutputConfiguration");
|
||||||
}
|
}
|
||||||
|
|
||||||
createCaptureSessionInternal(null, outputConfigurations, callback, handler);
|
createCaptureSessionInternal(null, outputConfigurations, callback, handler,
|
||||||
|
/*isConstrainedHighSpeed*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -494,13 +505,14 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
for (Surface surface : outputs) {
|
for (Surface surface : outputs) {
|
||||||
outConfigurations.add(new OutputConfiguration(surface));
|
outConfigurations.add(new OutputConfiguration(surface));
|
||||||
}
|
}
|
||||||
createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler);
|
createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
|
||||||
|
/*isConstrainedHighSpeed*/false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCaptureSessionInternal(InputConfiguration inputConfig,
|
private void createCaptureSessionInternal(InputConfiguration inputConfig,
|
||||||
List<OutputConfiguration> outputConfigurations,
|
List<OutputConfiguration> outputConfigurations,
|
||||||
CameraCaptureSession.StateCallback callback, Handler handler)
|
CameraCaptureSession.StateCallback callback, Handler handler,
|
||||||
throws CameraAccessException {
|
boolean isConstrainedHighSpeed) throws CameraAccessException {
|
||||||
synchronized(mInterfaceLock) {
|
synchronized(mInterfaceLock) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "createCaptureSessionInternal");
|
Log.d(TAG, "createCaptureSessionInternal");
|
||||||
@@ -508,6 +520,11 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
|
|
||||||
checkIfCameraClosedOrInError();
|
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
|
// 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
|
// After this call completes, the session is not allowed to call into CameraDeviceImpl
|
||||||
if (mCurrentSession != null) {
|
if (mCurrentSession != null) {
|
||||||
@@ -520,7 +537,8 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
Surface input = null;
|
Surface input = null;
|
||||||
try {
|
try {
|
||||||
// configure streams and then block until IDLE
|
// configure streams and then block until IDLE
|
||||||
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations);
|
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
|
||||||
|
isConstrainedHighSpeed);
|
||||||
if (inputConfig != null) {
|
if (inputConfig != null) {
|
||||||
input = new Surface();
|
input = new Surface();
|
||||||
mRemoteDevice.getInputSurface(/*out*/input);
|
mRemoteDevice.getInputSurface(/*out*/input);
|
||||||
@@ -545,7 +563,7 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
CameraCaptureSessionImpl newSession =
|
CameraCaptureSessionImpl newSession =
|
||||||
new CameraCaptureSessionImpl(mNextSessionId++, input,
|
new CameraCaptureSessionImpl(mNextSessionId++, input,
|
||||||
outSurfaces, callback, handler, this, mDeviceHandler,
|
outSurfaces, callback, handler, this, mDeviceHandler,
|
||||||
configureSuccess);
|
configureSuccess, isConstrainedHighSpeed);
|
||||||
|
|
||||||
// TODO: wait until current session closes, then create the new session
|
// TODO: wait until current session closes, then create the new session
|
||||||
mCurrentSession = newSession;
|
mCurrentSession = newSession;
|
||||||
@@ -1906,17 +1924,156 @@ public class CameraDeviceImpl extends CameraDevice {
|
|||||||
return mCharacteristics;
|
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
|
@Override
|
||||||
public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
|
public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
|
||||||
android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
|
android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
|
||||||
throws CameraAccessException {
|
throws CameraAccessException {
|
||||||
// TODO: to be implemented
|
if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
|
||||||
throw new UnsupportedOperationException("To be implemented!!!!");
|
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
|
@Override
|
||||||
public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
|
public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
|
||||||
throws CameraAccessException {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int endConfigure() {
|
public int endConfigure(boolean isConstrainedHighSpeed) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "endConfigure called.");
|
Log.d(TAG, "endConfigure called.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_SW_READ_OFTEN = 0x00000003;
|
||||||
private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100;
|
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_COMPOSER = 0x00000800;
|
||||||
|
private static final int GRALLOC_USAGE_HW_RENDER = 0x00000200;
|
||||||
private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
|
private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000;
|
||||||
|
|
||||||
public static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding
|
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;
|
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
|
* Query the surface for its currently configured usage flags
|
||||||
*/
|
*/
|
||||||
|
|||||||
80
core/java/android/hardware/camera2/utils/SurfaceUtils.java
Normal file
80
core/java/android/hardware/camera2/utils/SurfaceUtils.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user