Home | History | Annotate | Download | only in params
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package android.hardware.camera2.params;
     19 
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.annotation.SystemApi;
     23 import android.graphics.ImageFormat;
     24 import android.hardware.camera2.CameraCaptureSession;
     25 import android.hardware.camera2.CameraDevice;
     26 import android.hardware.camera2.utils.HashCodeHelpers;
     27 import android.hardware.camera2.utils.SurfaceUtils;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.util.Log;
     31 import android.util.Size;
     32 import android.view.Surface;
     33 
     34 import static com.android.internal.util.Preconditions.*;
     35 
     36 /**
     37  * A class for describing camera output, which contains a {@link Surface} and its specific
     38  * configuration for creating capture session.
     39  *
     40  * @see CameraDevice#createCaptureSessionByOutputConfiguration
     41  *
     42  */
     43 public final class OutputConfiguration implements Parcelable {
     44 
     45     /**
     46      * Rotation constant: 0 degree rotation (no rotation)
     47      *
     48      * @hide
     49      */
     50     @SystemApi
     51     public static final int ROTATION_0 = 0;
     52 
     53     /**
     54      * Rotation constant: 90 degree counterclockwise rotation.
     55      *
     56      * @hide
     57      */
     58     @SystemApi
     59     public static final int ROTATION_90 = 1;
     60 
     61     /**
     62      * Rotation constant: 180 degree counterclockwise rotation.
     63      *
     64      * @hide
     65      */
     66     @SystemApi
     67     public static final int ROTATION_180 = 2;
     68 
     69     /**
     70      * Rotation constant: 270 degree counterclockwise rotation.
     71      *
     72      * @hide
     73      */
     74     @SystemApi
     75     public static final int ROTATION_270 = 3;
     76 
     77     /**
     78      * Invalid surface group ID.
     79      *
     80      *<p>An {@link OutputConfiguration} with this value indicates that the included surface
     81      *doesn't belong to any surface group.</p>
     82      */
     83     public static final int SURFACE_GROUP_ID_NONE = -1;
     84 
     85     /**
     86      * Create a new {@link OutputConfiguration} instance with a {@link Surface}.
     87      *
     88      * @param surface
     89      *          A Surface for camera to output to.
     90      *
     91      * <p>This constructor creates a default configuration, with a surface group ID of
     92      * {@value #SURFACE_GROUP_ID_NONE}.</p>
     93      *
     94      */
     95     public OutputConfiguration(@NonNull Surface surface) {
     96         this(SURFACE_GROUP_ID_NONE, surface, ROTATION_0);
     97     }
     98 
     99     /**
    100      * Unknown surface source type.
    101      */
    102     private final int SURFACE_TYPE_UNKNOWN = -1;
    103 
    104     /**
    105      * The surface is obtained from {@link android.view.SurfaceView}.
    106      */
    107     private final int SURFACE_TYPE_SURFACE_VIEW = 0;
    108 
    109     /**
    110      * The surface is obtained from {@link android.graphics.SurfaceTexture}.
    111      */
    112     private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
    113 
    114     /**
    115      * Create a new {@link OutputConfiguration} instance with a {@link Surface},
    116      * with a surface group ID.
    117      *
    118      * <p>
    119      * A surface group ID is used to identify which surface group this output surface belongs to. A
    120      * surface group is a group of output surfaces that are not intended to receive camera output
    121      * buffer streams simultaneously. The {@link CameraDevice} may be able to share the buffers used
    122      * by all the surfaces from the same surface group, therefore may reduce the overall memory
    123      * footprint. The application should only set the same set ID for the streams that are not
    124      * simultaneously streaming. A negative ID indicates that this surface doesn't belong to any
    125      * surface group. The default value is {@value #SURFACE_GROUP_ID_NONE}.</p>
    126      *
    127      * <p>For example, a video chat application that has an adaptive output resolution feature would
    128      * need two (or more) output resolutions, to switch resolutions without any output glitches.
    129      * However, at any given time, only one output is active to minimize outgoing network bandwidth
    130      * and encoding overhead.  To save memory, the application should set the video outputs to have
    131      * the same non-negative group ID, so that the camera device can share the same memory region
    132      * for the alternating outputs.</p>
    133      *
    134      * <p>It is not an error to include output streams with the same group ID in the same capture
    135      * request, but the resulting memory consumption may be higher than if the two streams were
    136      * not in the same surface group to begin with, especially if the outputs have substantially
    137      * different dimensions.</p>
    138      *
    139      * @param surfaceGroupId
    140      *          A group ID for this output, used for sharing memory between multiple outputs.
    141      * @param surface
    142      *          A Surface for camera to output to.
    143      *
    144      */
    145     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface) {
    146         this(surfaceGroupId, surface, ROTATION_0);
    147     }
    148 
    149     /**
    150      * Create a new {@link OutputConfiguration} instance.
    151      *
    152      * <p>This constructor takes an argument for desired camera rotation</p>
    153      *
    154      * @param surface
    155      *          A Surface for camera to output to.
    156      * @param rotation
    157      *          The desired rotation to be applied on camera output. Value must be one of
    158      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
    159      *          application should make sure corresponding surface size has width and height
    160      *          transposed relative to the width and height without rotation. For example,
    161      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
    162      *          application should set rotation to {@code ROTATION_90} and make sure the
    163      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
    164      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
    165      * @hide
    166      */
    167     @SystemApi
    168     public OutputConfiguration(@NonNull Surface surface, int rotation) {
    169         this(SURFACE_GROUP_ID_NONE, surface, rotation);
    170     }
    171 
    172 
    173     /**
    174      * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
    175      *
    176      * <p>This constructor takes an argument for desired camera rotation and for the surface group
    177      * ID.  See {@link #OutputConfiguration(int, Surface)} for details of the group ID.</p>
    178      *
    179      * @param surfaceGroupId
    180      *          A group ID for this output, used for sharing memory between multiple outputs.
    181      * @param surface
    182      *          A Surface for camera to output to.
    183      * @param rotation
    184      *          The desired rotation to be applied on camera output. Value must be one of
    185      *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degrees,
    186      *          application should make sure corresponding surface size has width and height
    187      *          transposed relative to the width and height without rotation. For example,
    188      *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
    189      *          application should set rotation to {@code ROTATION_90} and make sure the
    190      *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
    191      *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
    192      * @hide
    193      */
    194     @SystemApi
    195     public OutputConfiguration(int surfaceGroupId, @NonNull Surface surface, int rotation) {
    196         checkNotNull(surface, "Surface must not be null");
    197         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
    198         mSurfaceGroupId = surfaceGroupId;
    199         mSurfaceType = SURFACE_TYPE_UNKNOWN;
    200         mSurface = surface;
    201         mRotation = rotation;
    202         mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
    203         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
    204         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
    205         mConfiguredGenerationId = surface.getGenerationId();
    206         mIsDeferredConfig = false;
    207     }
    208 
    209     /**
    210      * Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
    211      * source class.
    212      * <p>
    213      * This constructor takes an argument for desired Surface size and the Surface source class
    214      * without providing the actual output Surface. This is used to setup a output configuration
    215      * with a deferred Surface. The application can use this output configuration to create a
    216      * session.
    217      * </p>
    218      * <p>
    219      * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the
    220      * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration}
    221      * before submitting a request with this Surface target. The deferred Surface can only be
    222      * obtained from either from {@link android.view.SurfaceView} by calling
    223      * {@link android.view.SurfaceHolder#getSurface}, or from
    224      * {@link android.graphics.SurfaceTexture} via
    225      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
    226      * </p>
    227      *
    228      * @param surfaceSize Size for the deferred surface.
    229      * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
    230      *            this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
    231      *            {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
    232      * @hide
    233      */
    234     public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
    235         checkNotNull(klass, "surfaceSize must not be null");
    236         checkNotNull(klass, "klass must not be null");
    237         if (klass == android.view.SurfaceHolder.class) {
    238             mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
    239         } else if (klass == android.graphics.SurfaceTexture.class) {
    240             mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
    241         } else {
    242             mSurfaceType = SURFACE_TYPE_UNKNOWN;
    243             throw new IllegalArgumentException("Unknow surface source class type");
    244         }
    245 
    246         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
    247         mSurface = null;
    248         mRotation = ROTATION_0;
    249         mConfiguredSize = surfaceSize;
    250         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
    251         mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
    252         mConfiguredGenerationId = 0;
    253         mIsDeferredConfig = true;
    254     }
    255 
    256     /**
    257      * Check if this configuration has deferred configuration.
    258      *
    259      * <p>This will return true if the output configuration was constructed with surface deferred.
    260      * It will return true even after the deferred surface is set later.</p>
    261      *
    262      * @return true if this configuration has deferred surface.
    263      * @hide
    264      */
    265     public boolean isDeferredConfiguration() {
    266         return mIsDeferredConfig;
    267     }
    268 
    269     /**
    270      * Set the deferred surface to this OutputConfiguration.
    271      *
    272      * <p>
    273      * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by
    274      * calling {@link android.view.SurfaceHolder#getSurface}, or from
    275      * {@link android.graphics.SurfaceTexture} via
    276      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred
    277      * surface is set, the application must finish the deferred surface configuration via
    278      * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with
    279      * this surface target.
    280      * </p>
    281      *
    282      * @param surface The deferred surface to be set.
    283      * @throws IllegalArgumentException if the Surface is invalid.
    284      * @throws IllegalStateException if a Surface was already set to this deferred
    285      *         OutputConfiguration.
    286      * @hide
    287      */
    288     public void setDeferredSurface(@NonNull Surface surface) {
    289         checkNotNull(surface, "Surface must not be null");
    290         if (mSurface != null) {
    291             throw new IllegalStateException("Deferred surface is already set!");
    292         }
    293 
    294         // This will throw IAE is the surface was abandoned.
    295         Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
    296         if (!surfaceSize.equals(mConfiguredSize)) {
    297             Log.w(TAG, "Deferred surface size " + surfaceSize +
    298                     " is different with pre-configured size " + mConfiguredSize +
    299                     ", the pre-configured size will be used.");
    300         }
    301 
    302         mSurface = surface;
    303     }
    304 
    305     /**
    306      * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
    307      * instance.
    308      *
    309      * @param other Another {@link OutputConfiguration} instance to be copied.
    310      *
    311      * @hide
    312      */
    313     public OutputConfiguration(@NonNull OutputConfiguration other) {
    314         if (other == null) {
    315             throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
    316         }
    317 
    318         this.mSurface = other.mSurface;
    319         this.mRotation = other.mRotation;
    320         this.mSurfaceGroupId = other.mSurfaceGroupId;
    321         this.mSurfaceType = other.mSurfaceType;
    322         this.mConfiguredDataspace = other.mConfiguredDataspace;
    323         this.mConfiguredFormat = other.mConfiguredFormat;
    324         this.mConfiguredSize = other.mConfiguredSize;
    325         this.mConfiguredGenerationId = other.mConfiguredGenerationId;
    326         this.mIsDeferredConfig = other.mIsDeferredConfig;
    327     }
    328 
    329     /**
    330      * Create an OutputConfiguration from Parcel.
    331      */
    332     private OutputConfiguration(@NonNull Parcel source) {
    333         int rotation = source.readInt();
    334         int surfaceSetId = source.readInt();
    335         int surfaceType = source.readInt();
    336         int width = source.readInt();
    337         int height = source.readInt();
    338         Surface surface = Surface.CREATOR.createFromParcel(source);
    339         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
    340         mSurfaceGroupId = surfaceSetId;
    341         mSurface = surface;
    342         mRotation = rotation;
    343         if (surface != null) {
    344             mSurfaceType = SURFACE_TYPE_UNKNOWN;
    345             mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
    346             mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
    347             mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
    348             mConfiguredGenerationId = mSurface.getGenerationId();
    349             mIsDeferredConfig = true;
    350         } else {
    351             mSurfaceType = surfaceType;
    352             mConfiguredSize = new Size(width, height);
    353             mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
    354             mConfiguredGenerationId = 0;
    355             mConfiguredDataspace =
    356                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
    357             mIsDeferredConfig = false;
    358         }
    359     }
    360 
    361     /**
    362      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
    363      *
    364      * @return the {@link Surface} associated with this {@link OutputConfiguration}.
    365      */
    366     @NonNull
    367     public Surface getSurface() {
    368         return mSurface;
    369     }
    370 
    371     /**
    372      * Get the rotation associated with this {@link OutputConfiguration}.
    373      *
    374      * @return the rotation associated with this {@link OutputConfiguration}.
    375      *         Value will be one of ROTATION_[0, 90, 180, 270]
    376      *
    377      * @hide
    378      */
    379     @SystemApi
    380     public int getRotation() {
    381         return mRotation;
    382     }
    383 
    384     /**
    385      * Get the surface group ID associated with this {@link OutputConfiguration}.
    386      *
    387      * @return the surface group ID associated with this {@link OutputConfiguration}.
    388      *         The default value is {@value #SURFACE_GROUP_ID_NONE}.
    389      */
    390     public int getSurfaceGroupId() {
    391         return mSurfaceGroupId;
    392     }
    393 
    394     public static final Parcelable.Creator<OutputConfiguration> CREATOR =
    395             new Parcelable.Creator<OutputConfiguration>() {
    396         @Override
    397         public OutputConfiguration createFromParcel(Parcel source) {
    398             try {
    399                 OutputConfiguration outputConfiguration = new OutputConfiguration(source);
    400                 return outputConfiguration;
    401             } catch (Exception e) {
    402                 Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
    403                 return null;
    404             }
    405         }
    406 
    407         @Override
    408         public OutputConfiguration[] newArray(int size) {
    409             return new OutputConfiguration[size];
    410         }
    411     };
    412 
    413     @Override
    414     public int describeContents() {
    415         return 0;
    416     }
    417 
    418     @Override
    419     public void writeToParcel(Parcel dest, int flags) {
    420         if (dest == null) {
    421             throw new IllegalArgumentException("dest must not be null");
    422         }
    423         dest.writeInt(mRotation);
    424         dest.writeInt(mSurfaceGroupId);
    425         dest.writeInt(mSurfaceType);
    426         dest.writeInt(mConfiguredSize.getWidth());
    427         dest.writeInt(mConfiguredSize.getHeight());
    428         if (mSurface != null) {
    429             mSurface.writeToParcel(dest, flags);
    430         }
    431     }
    432 
    433     /**
    434      * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
    435      *
    436      * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
    437      * properties (width, height, format, dataspace) when the output configurations are created,
    438      * and all other configuration parameters are equal. </p>
    439      *
    440      * @return {@code true} if the objects were equal, {@code false} otherwise
    441      */
    442     @Override
    443     public boolean equals(Object obj) {
    444         if (obj == null) {
    445             return false;
    446         } else if (this == obj) {
    447             return true;
    448         } else if (obj instanceof OutputConfiguration) {
    449             final OutputConfiguration other = (OutputConfiguration) obj;
    450             boolean iSSurfaceEqual = mSurface == other.mSurface &&
    451                     mConfiguredGenerationId == other.mConfiguredGenerationId ;
    452             if (mIsDeferredConfig) {
    453                 Log.i(TAG, "deferred config has the same surface");
    454                 iSSurfaceEqual = true;
    455             }
    456             return mRotation == other.mRotation &&
    457                    iSSurfaceEqual&&
    458                    mConfiguredSize.equals(other.mConfiguredSize) &&
    459                    mConfiguredFormat == other.mConfiguredFormat &&
    460                    mConfiguredDataspace == other.mConfiguredDataspace &&
    461                    mSurfaceGroupId == other.mSurfaceGroupId &&
    462                    mSurfaceType == other.mSurfaceType &&
    463                    mIsDeferredConfig == other.mIsDeferredConfig;
    464         }
    465         return false;
    466     }
    467 
    468     /**
    469      * {@inheritDoc}
    470      */
    471     @Override
    472     public int hashCode() {
    473         // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise
    474         // The deferred output configuration will be lost in the camera streammap after the deferred
    475         // surface is set.
    476         if (mIsDeferredConfig) {
    477             return HashCodeHelpers.hashCode(
    478                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
    479                     mSurfaceGroupId, mSurfaceType);
    480         }
    481 
    482         return HashCodeHelpers.hashCode(
    483             mRotation, mSurface.hashCode(), mConfiguredGenerationId,
    484             mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
    485     }
    486 
    487     private static final String TAG = "OutputConfiguration";
    488     private Surface mSurface;
    489     private final int mRotation;
    490     private final int mSurfaceGroupId;
    491     // Surface source type, this is only used by the deferred surface configuration objects.
    492     private final int mSurfaceType;
    493 
    494     // The size, format, and dataspace of the surface when OutputConfiguration is created.
    495     private final Size mConfiguredSize;
    496     private final int mConfiguredFormat;
    497     private final int mConfiguredDataspace;
    498     // Surface generation ID to distinguish changes to Surface native internals
    499     private final int mConfiguredGenerationId;
    500     // Flag indicating if this config has deferred surface.
    501     private final boolean mIsDeferredConfig;
    502 }
    503