/* * Copyright 2023 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.companion.virtual.camera; import static java.util.Objects.requireNonNull; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.companion.virtual.VirtualDevice; import android.companion.virtual.flags.Flags; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.hardware.camera2.CameraMetadata; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; import android.view.Surface; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; import java.util.concurrent.Executor; /** * Configuration to create a new {@link VirtualCamera}. * *
Instance of this class are created using the {@link VirtualCameraConfig.Builder}.
*
* @hide
*/
@SystemApi
@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
public final class VirtualCameraConfig implements Parcelable {
private static final int LENS_FACING_UNKNOWN = -1;
/**
* Sensor orientation of {@code 0} degrees.
* @see #getSensorOrientation
*/
public static final int SENSOR_ORIENTATION_0 = 0;
/**
* Sensor orientation of {@code 90} degrees.
* @see #getSensorOrientation
*/
public static final int SENSOR_ORIENTATION_90 = 90;
/**
* Sensor orientation of {@code 180} degrees.
* @see #getSensorOrientation
*/
public static final int SENSOR_ORIENTATION_180 = 180;
/**
* Sensor orientation of {@code 270} degrees.
* @see #getSensorOrientation
*/
public static final int SENSOR_ORIENTATION_270 = 270;
/** @hide */
@IntDef(prefix = {"SENSOR_ORIENTATION_"}, value = {
SENSOR_ORIENTATION_0,
SENSOR_ORIENTATION_90,
SENSOR_ORIENTATION_180,
SENSOR_ORIENTATION_270
})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorOrientation {}
private final String mName;
private final Set To build an instance of {@link VirtualCameraConfig} the following conditions must be met:
* At least one {@link VirtualCameraStreamConfig} must be added.
*
* @param width The width of the stream.
* @param height The height of the stream.
* @param format The input format of the stream. Supported formats are
* {@link ImageFormat#YUV_420_888} and {@link PixelFormat#RGBA_8888}.
* @param maximumFramesPerSecond The maximum frame rate (in frames per second) for the
* stream.
*
* @throws IllegalArgumentException if invalid dimensions, format or frame rate are passed.
*/
@NonNull
public Builder addStreamConfig(
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@ImageFormat.Format int format,
@IntRange(from = 1) int maximumFramesPerSecond) {
if (width <= 0) {
throw new IllegalArgumentException(
"Invalid width passed for stream config: " + width
+ ", must be greater than 0");
}
if (height <= 0) {
throw new IllegalArgumentException(
"Invalid height passed for stream config: " + height
+ ", must be greater than 0");
}
if (!isFormatSupported(format)) {
throw new IllegalArgumentException(
"Invalid format passed for stream config: " + format);
}
if (maximumFramesPerSecond <= 0
|| maximumFramesPerSecond > VirtualCameraStreamConfig.MAX_FPS_UPPER_LIMIT) {
throw new IllegalArgumentException(
"Invalid maximumFramesPerSecond, must be greater than 0 and less than "
+ VirtualCameraStreamConfig.MAX_FPS_UPPER_LIMIT);
}
mStreamConfigurations.add(new VirtualCameraStreamConfig(width, height, format,
maximumFramesPerSecond));
return this;
}
/**
* Sets the sensor orientation of the virtual camera. This field is optional and can be
* omitted (defaults to {@link #SENSOR_ORIENTATION_0}).
*
* @param sensorOrientation The sensor orientation of the camera, which represents the
* clockwise angle (in degrees) through which the output image
* needs to be rotated to be upright on the device screen in its
* native orientation.
*/
@NonNull
public Builder setSensorOrientation(@SensorOrientation int sensorOrientation) {
if (sensorOrientation != SENSOR_ORIENTATION_0
&& sensorOrientation != SENSOR_ORIENTATION_90
&& sensorOrientation != SENSOR_ORIENTATION_180
&& sensorOrientation != SENSOR_ORIENTATION_270) {
throw new IllegalArgumentException(
"Invalid sensor orientation: " + sensorOrientation);
}
mSensorOrientation = sensorOrientation;
return this;
}
/**
* Sets the lens facing direction of the virtual camera, can be either
* {@link CameraMetadata#LENS_FACING_FRONT} or {@link CameraMetadata#LENS_FACING_BACK}.
*
* A {@link VirtualDevice} can have at most one {@link VirtualCamera} with
* {@link CameraMetadata#LENS_FACING_FRONT} and at most one {@link VirtualCamera} with
* {@link CameraMetadata#LENS_FACING_BACK}.
*
* @param lensFacing The direction that the virtual camera faces relative to the device's
* screen.
*/
@NonNull
public Builder setLensFacing(int lensFacing) {
if (lensFacing != CameraMetadata.LENS_FACING_BACK
&& lensFacing != CameraMetadata.LENS_FACING_FRONT) {
throw new IllegalArgumentException("Unsupported lens facing: " + lensFacing);
}
mLensFacing = lensFacing;
return this;
}
/**
* Sets the {@link VirtualCameraCallback} used by the framework to communicate with the
* {@link VirtualCamera} owner.
*
* Setting a callback is mandatory.
*
* @param executor The executor onto which the callback methods will be called
* @param callback The instance of the callback to be added. Subsequent call to this method
* will replace the callback set.
*/
@NonNull
@SuppressLint("MissingGetterMatchingBuilder") // The configuration is immutable
public Builder setVirtualCameraCallback(
@NonNull Executor executor, @NonNull VirtualCameraCallback callback) {
mCallbackExecutor = requireNonNull(executor);
mCallback = requireNonNull(callback);
return this;
}
/**
* Builds a new instance of {@link VirtualCameraConfig}
*
* @throws NullPointerException if some required parameters are missing.
* @throws IllegalArgumentException if any parameter is invalid.
*/
@NonNull
public VirtualCameraConfig build() {
return new VirtualCameraConfig(
mName, mStreamConfigurations, mCallbackExecutor, mCallback, mSensorOrientation,
mLensFacing);
}
}
private static class VirtualCameraCallbackInternal extends IVirtualCameraCallback.Stub {
private final VirtualCameraCallback mCallback;
private final Executor mExecutor;
private VirtualCameraCallbackInternal(VirtualCameraCallback callback, Executor executor) {
mCallback = callback;
mExecutor = executor;
}
@Override
public void onStreamConfigured(int streamId, Surface surface, int width, int height,
int format) {
mExecutor.execute(() -> mCallback.onStreamConfigured(streamId, surface, width, height,
format));
}
@Override
public void onProcessCaptureRequest(int streamId, long frameId) {
mExecutor.execute(() -> mCallback.onProcessCaptureRequest(streamId, frameId));
}
@Override
public void onStreamClosed(int streamId) {
mExecutor.execute(() -> mCallback.onStreamClosed(streamId));
}
}
@NonNull
public static final Parcelable.Creator