Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8f95457 authored by Jyoti Bhayana's avatar Jyoti Bhayana Committed by Android (Google) Code Review
Browse files

Merge "Camera2 multi-client support" into main

parents 61a54bd9 5f0c0f0d
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -417,6 +417,7 @@ public abstract class CameraDevice implements AutoCloseable {
     *                                  or if any of the output configurations sets a stream use
     *                                  case different from {@link
     *                                  android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
     * @throws UnsupportedOperationException if the camera has been opened in shared mode
     * @see CameraExtensionCharacteristics#getSupportedExtensions
     * @see CameraExtensionCharacteristics#getExtensionSupportedSizes
     */
@@ -1258,7 +1259,8 @@ public abstract class CameraDevice implements AutoCloseable {
     *                                  configurations are empty; or the session configuration
     *                                  executor is invalid;
     *                                  or the output dynamic range combination is
     *                                  invalid/unsupported.
     *                                  invalid/unsupported; or the session type is not shared when
     *                                  camera has been opened in shared mode.
     * @throws CameraAccessException In case the camera device is no longer connected or has
     *                               encountered a fatal error.
     * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -1292,6 +1294,8 @@ public abstract class CameraDevice implements AutoCloseable {
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error
     * @throws IllegalStateException if the camera device has been closed
     * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
     *                                       shared mode
     */
    @NonNull
    public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
@@ -1328,6 +1332,8 @@ public abstract class CameraDevice implements AutoCloseable {
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error
     * @throws IllegalStateException if the camera device has been closed
     * @throws UnsupportedOperationException if this is not a primary client of a camera opened in
     *                                       shared mode
     *
     * @see #TEMPLATE_PREVIEW
     * @see #TEMPLATE_RECORD
@@ -1369,6 +1375,7 @@ public abstract class CameraDevice implements AutoCloseable {
     * @throws CameraAccessException if the camera device is no longer connected or has
     *                               encountered a fatal error
     * @throws IllegalStateException if the camera device has been closed
     * @throws UnsupportedOperationException if the camera has been opened in shared mode
     *
     * @see CaptureRequest.Builder
     * @see TotalCaptureResult
+7 −0
Original line number Diff line number Diff line
@@ -1375,6 +1375,9 @@ public final class CameraManager {
     * @throws SecurityException if the application does not have permission to
     *                           access the camera
     *
     * @throws UnsupportedOperationException if {@link #isCameraDeviceSharingSupported} returns
     *                                       false for the given {@code cameraId}.
     *
     * @see #getCameraIdList
     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
     *
@@ -1393,6 +1396,10 @@ public final class CameraManager {
        if (executor == null) {
            throw new IllegalArgumentException("executor was null");
        }
        if (!isCameraDeviceSharingSupported(cameraId)) {
            throw new UnsupportedOperationException(
                    "CameraDevice sharing is not supported for Camera ID: " + cameraId);
        }
        openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0,
                getRotationOverride(mContext), /*sharedMode*/true);
    }
+18 −0
Original line number Diff line number Diff line
@@ -299,6 +299,24 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
        return mRequestType;
    }

    /**
     * Get the stream ids corresponding to the target surfaces.
     *
     * @hide
     */
    public int[] getStreamIds() {
        return mStreamIdxArray;
    };

    /**
     * Get the surface ids corresponding to the target surfaces.
     *
     * @hide
     */
    public int[] getSurfaceIds() {
        return mSurfaceIdxArray;
    };

    // If this request is part of constrained high speed request list that was created by
    // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
    private boolean mIsPartOfCHSRequestList = false;
+24 −0
Original line number Diff line number Diff line
@@ -340,6 +340,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
        }
    }

    /**
     * Shared Camera capture session API which can be used by the clients
     * to start streaming.
     *
     * @hide
     */
    public int startStreaming(List<Surface> surfaces, Executor executor,
            CaptureCallback callback) throws CameraAccessException {

        synchronized (mDeviceImpl.mInterfaceLock) {
            checkNotClosed();

            executor = CameraDeviceImpl.checkExecutor(executor, callback);

            if (DEBUG) {
                Log.v(TAG, mIdString + "startStreaming callback " + callback + " executor"
                        + " " + executor);
            }

            return addPendingSequence(mDeviceImpl.startStreaming(surfaces,
                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
        }
    }

    private void checkRepeatingRequest(CaptureRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("request must not be null");
+116 −24
Original line number Diff line number Diff line
@@ -451,6 +451,16 @@ public class CameraDeviceImpl extends CameraDevice
        }
    }

    /**
     * When camera device is opened in shared mode, call to check if this is a primary client.
     *
     */
    public boolean isPrimaryClient() {
        synchronized (mInterfaceLock) {
            return mIsPrimaryClient;
        }
    }

    private Map<String, CameraCharacteristics> getPhysicalIdToChars() {
        if (mPhysicalIdsToChars == null) {
            try {
@@ -858,24 +868,19 @@ public class CameraDeviceImpl extends CameraDevice
        List<SharedOutputConfiguration> sharedConfigs =
                sharedSessionConfiguration.getOutputStreamsInformation();
        for (SharedOutputConfiguration sharedConfig : sharedConfigs) {
            if (outConfig.getConfiguredSize().equals(sharedConfig.getSize())
                    && (outConfig.getConfiguredFormat() == sharedConfig.getFormat())
                    && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
                    && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType())
            if ((outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE)
                    && (outConfig.getMirrorMode() == sharedConfig.getMirrorMode())
                    && (outConfig.getUsage() == sharedConfig.getUsage())
                    && (outConfig.isReadoutTimestampEnabled()
                    == sharedConfig.isReadoutTimestampEnabled())
                    && (outConfig.getTimestampBase() == sharedConfig.getTimestampBase())
                    && (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase())
                    && (outConfig.getColorSpace().equals(
                    sharedSessionConfiguration.getColorSpace()))
                    && (outConfig.getDynamicRangeProfile()
                    == DynamicRangeProfiles.STANDARD)
                    && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace())
                    && (Objects.equals(outConfig.getPhysicalCameraId(),
                    sharedConfig.getPhysicalCameraId()))
                    && (outConfig.getSensorPixelModes().isEmpty())
                    && (!outConfig.isMultiResolution())
                    && (!outConfig.isDeferredConfiguration())
                    && (!outConfig.isShared())) {
                //Found valid config, return true
                return true;
@@ -907,14 +912,6 @@ public class CameraDeviceImpl extends CameraDevice
        if (config.getExecutor() == null) {
            throw new IllegalArgumentException("Invalid executor");
        }
        if (mSharedMode) {
            if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) {
                throw new IllegalArgumentException("Invalid session type");
            }
            if (!checkSharedSessionConfiguration(outputConfigs)) {
                throw new IllegalArgumentException("Invalid output configurations");
            }
        }
        createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
                config.getStateCallback(), config.getExecutor(), config.getSessionType(),
                config.getSessionParameters());
@@ -932,17 +929,26 @@ public class CameraDeviceImpl extends CameraDevice

            checkIfCameraClosedOrInError();

            boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
            if (Flags.cameraMultiClient() && mSharedMode) {
                if (!isSharedSession) {
                    throw new IllegalArgumentException("Invalid session type");
                }
                if (!checkSharedSessionConfiguration(outputConfigurations)) {
                    throw new IllegalArgumentException("Invalid output configurations");
                }
                if (inputConfig != null) {
                    throw new IllegalArgumentException("Shared capture session doesn't support"
                            + " input configuration yet.");
                }
            }

            boolean isConstrainedHighSpeed =
                    (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
            if (isConstrainedHighSpeed && inputConfig != null) {
                throw new IllegalArgumentException("Constrained high speed session doesn't support"
                        + " input configuration yet.");
            }
            boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE);
            if (isSharedSession && inputConfig != null) {
                throw new IllegalArgumentException("Shared capture session doesn't support"
                        + " input configuration yet.");
            }

            if (mCurrentExtensionSession != null) {
                mCurrentExtensionSession.commitStats();
@@ -1004,8 +1010,7 @@ public class CameraDeviceImpl extends CameraDevice
                        mCharacteristics);
            } else if (isSharedSession) {
                newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++,
                        callback, executor, this, mDeviceExecutor, configureSuccess,
                        mIsPrimaryClient);
                        callback, executor, this, mDeviceExecutor, configureSuccess);
            } else {
                newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
                        callback, executor, this, mDeviceExecutor, configureSuccess);
@@ -1074,6 +1079,11 @@ public class CameraDeviceImpl extends CameraDevice
        synchronized(mInterfaceLock) {
            checkIfCameraClosedOrInError();

            if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
                throw new UnsupportedOperationException("In shared session mode,"
                        + "only primary clients can create capture request.");
            }

            for (String physicalId : physicalCameraIdSet) {
                if (Objects.equals(physicalId, getId())) {
                    throw new IllegalStateException("Physical id matches the logical id!");
@@ -1100,6 +1110,11 @@ public class CameraDeviceImpl extends CameraDevice
        synchronized(mInterfaceLock) {
            checkIfCameraClosedOrInError();

            if (Flags.cameraMultiClient() && mSharedMode && !mIsPrimaryClient) {
                throw new UnsupportedOperationException("In shared session mode,"
                        + "only primary clients can create capture request.");
            }

            CameraMetadataNative templatedRequest = null;

            templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
@@ -1119,6 +1134,10 @@ public class CameraDeviceImpl extends CameraDevice
            throws CameraAccessException {
        synchronized(mInterfaceLock) {
            checkIfCameraClosedOrInError();
            if (Flags.cameraMultiClient() && mSharedMode) {
                throw new UnsupportedOperationException("In shared session mode,"
                        + "reprocess capture requests are not supported.");
            }

            CameraMetadataNative resultMetadata = new
                    CameraMetadataNative(inputResult.getNativeCopy());
@@ -1572,6 +1591,74 @@ public class CameraDeviceImpl extends CameraDevice
        }
    }

    public int startStreaming(List<Surface> surfaces, CaptureCallback callback,
            Executor executor) throws CameraAccessException {
        // Need a valid executor, or current thread needs to have a looper, if
        // callback is valid
        executor = checkExecutor(executor, callback);
        synchronized (mInterfaceLock) {
            checkIfCameraClosedOrInError();
            for (Surface surface : surfaces) {
                if (surface == null) {
                    throw new IllegalArgumentException("Null Surface targets are not allowed");
                }
            }
            // In shared session mode, if there are other active clients streaming then
            // stoprepeating does not actually send request to HAL to cancel the request.
            // Cameraservice will use this call to remove this client surfaces provided in its
            // previous streaming request. If this is the only client for the shared camera device
            // then camerservice will ask HAL to cancel the previous repeating request
            stopRepeating();

            // StartStreaming API does not allow capture parameters to be provided through a capture
            // request. If the primary client has an existing repeating request, the camera service
            // will either attach the provided surfaces to that request or create a default capture
            // request if no repeating request is active. A default capture request is created here
            // for initial use. The capture callback will provide capture results that include the
            // actual capture parameters used for the streaming.
            CaptureRequest.Builder builder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            for (Surface surface : surfaces) {
                builder.addTarget(surface);
            }
            CaptureRequest request = builder.build();
            request.convertSurfaceToStreamId(mConfiguredOutputs);

            SubmitInfo requestInfo;
            requestInfo = mRemoteDevice.startStreaming(request.getStreamIds(),
                    request.getSurfaceIds());
            request.recoverStreamIdToSurface();
            List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
            requestList.add(request);

            if (callback != null) {
                mCaptureCallbackMap.put(requestInfo.getRequestId(),
                        new CaptureCallbackHolder(
                            callback, requestList, executor, true, mNextSessionId - 1));
            } else {
                if (DEBUG) {
                    Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
                }
            }

            if (mRepeatingRequestId != REQUEST_ID_NONE) {
                checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
                        requestInfo.getLastFrameNumber(), mRepeatingRequestTypes);
            }

            CaptureRequest[] requestArray = requestList.toArray(
                    new CaptureRequest[requestList.size()]);
            mRepeatingRequestId = requestInfo.getRequestId();
            mRepeatingRequestTypes = getRequestTypes(requestArray);

            if (mIdle) {
                mDeviceExecutor.execute(mCallOnActive);
            }
            mIdle = false;

            return requestInfo.getRequestId();
        }
    }

    public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
            Executor executor) throws CameraAccessException {
        List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
@@ -2894,6 +2981,11 @@ public class CameraDeviceImpl extends CameraDevice
    @Override
    public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
            throws CameraAccessException {
        if (Flags.cameraMultiClient() && mSharedMode) {
            throw new UnsupportedOperationException("In shared session mode,"
                    + "extension sessions are not supported.");
        }

        HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
                getPhysicalIdToChars());
        characteristicsMap.put(mCameraId, mCharacteristics);
Loading